模拟CREATE DATABASE如果没有为PostgreSQL提供?

Ama*_*tam 92 sql database postgresql ddl jdbc

我想创建一个通过JDBC不存在的数据库.与MySQL不同,PostgreSQL不支持create if not exists语法.完成此任务的最佳方法是什么?

应用程序不知道数据库是否存在.它应该检查,如果数据库存在,应该使用它.因此,连接到所需数据库是有意义的,如果由于数据库不存在而导致连接失败,则应创建新数据库(通过连接到默认postgres数据库).我检查了Postgres返回的错误代码,但我找不到任何相同的相关代码.

实现此目的的另一种方法是连接到postgres数据库并检查是否存在所需的数据库并相应地采取措施.第二个是锻炼有点乏味.

有什么方法可以在Postgres中实现这个功能吗?

小智 111

另一个替代方案,以防你想要一个shell脚本创建数据库(如果它不存在),否则只是保持原样:

psql -U postgres -tc "SELECT 1 FROM pg_database WHERE datname = 'my_db'" | grep -q 1 || psql -U postgres -c "CREATE DATABASE my_db"
Run Code Online (Sandbox Code Playgroud)

我发现这对devops配置脚本​​很有用,您可能希望在同一个实例上多次运行.

  • 您的路径中没有 `grep`。在 Windows 上,默认情况下不安装 `grep`。您可以搜索 `gnu grep windows` 以找到可以在 Windows 上运行的版本。 (2认同)

Erw*_*ter 76

您可以询问系统目录.棘手的部分(如已评论)pg_database只能作为单个语句执行.每个文件:

CREATE DATABASE 无法在事务块内执行.

因此它不能在函数或CREATE DATABASE语句中运行,它将隐式地位于事务块中.这可以通过使用DO连接回当前数据库来避开,该数据库在事务块之外运行.因此也无法回滚效果.

您需要安装附加模块dblink(每个db一次):

然后:

SELECT 'CREATE DATABASE mydb'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec
Run Code Online (Sandbox Code Playgroud)

有关其工作原理的详细说明:

用Postgres 9.3测试.你可以将它作为重复使用的功能.


And*_*kin 26

如果你不关心数据,你可以先删除数据库,然后重新创建它:

DROP DATABASE IF EXISTS dbname;
CREATE DATABASE dbname;
Run Code Online (Sandbox Code Playgroud)

  • 非常优雅的解决方案。如果您_确实_关心数据,请不要忘记先备份数据库。对于测试情况,这是我的首选解决方案。 (2认同)

Pal*_*ali 16

PostgreSQL 不支持IF NOT EXISTSforCREATE DATABASE语句。它仅在CREATE SCHEMA. 而且CREATE DATABASE不能在事务中发出,因此它不能在DO具有异常捕获的块中。

CREATE SCHEMA IF NOT EXISTS发出并且模式已经存在时,会引发带有重复对象信息的通知(不是错误)。

要解决这些问题,您需要使用dblink扩展程序,它会打开与数据库服务器的新连接并在不进入事务的情况下执行查询。您可以通过提供空字符串重用连接参数。

下面是PL/pgSQL完全模拟CREATE DATABASE IF NOT EXISTSCREATE SCHEMA IF NOT EXISTS. 它调用CREATE DATABASEvia dblink, catchduplicate_database异常(当数据库已经存在时发出)并通过传播将其转换为通知errcode。字符串消息, skipping以与它相同的方式附加CREATE SCHEMA IF NOT EXISTS

CREATE EXTENSION IF NOT EXISTS dblink;

DO $$
BEGIN
PERFORM dblink_exec('', 'CREATE DATABASE testdb');
EXCEPTION WHEN duplicate_database THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END
$$;
Run Code Online (Sandbox Code Playgroud)

该解决方案没有任何竞争条件,就像其他答案一样,其中数据库可以由外部进程(或同一脚本的其他实例)在检查数据库是否存在和它自己的创建之间创建。

此外,当CREATE DATABASE因其他错误而不是数据库已经存在而失败时,此错误将作为错误传播,而不是静默丢弃。只有捕获duplicate_database错误。所以它的行为确实IF NOT EXISTS应该如此。

您可以将此代码放入自己的函数中,直接调用或从事务中调用。只是回滚(恢复删除的数据库)是行不通的。

测试输出(通过 DO 调用两次,然后直接调用):

$ sudo -u postgres psql
psql (9.6.12)
Type "help" for help.

postgres=# \set ON_ERROR_STOP on
postgres=# \set VERBOSITY verbose
postgres=# 
postgres=# CREATE EXTENSION IF NOT EXISTS dblink;
CREATE EXTENSION
postgres=# DO $$
postgres$# BEGIN
postgres$# PERFORM dblink_exec('', 'CREATE DATABASE testdb');
postgres$# EXCEPTION WHEN duplicate_database THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
DO
postgres=# 
postgres=# CREATE EXTENSION IF NOT EXISTS dblink;
NOTICE:  42710: extension "dblink" already exists, skipping
LOCATION:  CreateExtension, extension.c:1539
CREATE EXTENSION
postgres=# DO $$
postgres$# BEGIN
postgres$# PERFORM dblink_exec('', 'CREATE DATABASE testdb');
postgres$# EXCEPTION WHEN duplicate_database THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
NOTICE:  42P04: database "testdb" already exists, skipping
LOCATION:  exec_stmt_raise, pl_exec.c:3165
DO
postgres=# 
postgres=# CREATE DATABASE testdb;
ERROR:  42P04: database "testdb" already exists
LOCATION:  createdb, dbcommands.c:467
Run Code Online (Sandbox Code Playgroud)

  • 好吧,其他答案并不那么精确,无法处理所有可能发生的极端情况。您还可以并行多次调用我的 PL/pgSQL 代码,并且它不会失败。 (2认同)

Ort*_*ier 7

我不得不使用稍微扩展的版本@Erwin Brandstetter:

DO
$do$
DECLARE
  _db TEXT := 'some_db';
  _user TEXT := 'postgres_user';
  _password TEXT := 'password';
BEGIN
  CREATE EXTENSION IF NOT EXISTS dblink; -- enable extension 
  IF EXISTS (SELECT 1 FROM pg_database WHERE datname = _db) THEN
    RAISE NOTICE 'Database already exists';
  ELSE
    PERFORM dblink_connect('host=localhost user=' || _user || ' password=' || _password || ' dbname=' || current_database());
    PERFORM dblink_exec('CREATE DATABASE ' || _db);
  END IF;
END
$do$
Run Code Online (Sandbox Code Playgroud)

我必须启用dblink扩展,另外我必须提供dblink的凭据.适用于Postgres 9.4.


Ahm*_*bas 6

最好的方法就是运行 SQL。

CREATE DATABASE MY_DATABASE; 
Run Code Online (Sandbox Code Playgroud)

如果数据库已经存在,它会抛出“数据库已存在错误”,你可以做任何你想做的事情,否则它会创建数据库。我不认为它会在你的数据库之上创建一个新的数据库。:D


wen*_*ner 5

如果您可以使用shell,请尝试

psql -U postgres -c 'select 1' -d $DB &>dev/null || psql -U postgres -tc 'create database $DB'
Run Code Online (Sandbox Code Playgroud)

我认为psql -U postgres -c "select 1" -d $DB比 更容易SELECT 1 FROM pg_database WHERE datname = 'my_db',并且只需要一种类型的引用,更容易与sh -c.

我在我的 ansible 任务中使用它

- name: create service database
  shell: docker exec postgres sh -c '{ psql -U postgres -tc "SELECT 1" -d {{service_name}} &> /dev/null && echo -n 1; } || { psql -U postgres -c "CREATE DATABASE {{service_name}}"}'
  register: shell_result
  changed_when: "shell_result.stdout != '1'"
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

88379 次

最近记录:

6 年,2 月 前