create if not exists
语法。如何实现创建不存在的数据库呢?应用程序并不知道该数据库是否存在。它应该检查,如果数据库存在就使用它。因此,连接到所需的数据库,如果由于数据库不存在而连接失败,则应该连接到默认的
postgres
数据库创建新数据库。我检查了Postgres返回的错误代码,但没有找到任何相关的代码。另一种方法是连接到
postgres
数据库并检查所需的数据库是否存在,并根据情况采取行动。第二种方法比较繁琐。在Postgres中有没有实现这种功能的方法?create if not exists
语法。如何实现创建不存在的数据库呢?postgres
数据库创建新数据库。我检查了Postgres返回的错误代码,但没有找到任何相关的代码。postgres
数据库并检查所需的数据库是否存在,并根据情况采取行动。第二种方法比较繁琐。在Postgres中有没有实现这种功能的方法?您可以查询系统目录pg_database
,该目录可从同一数据库集群中的任何数据库访问。棘手的部分是,CREATE DATABASE
只能作为单个语句执行。手册:
CREATE DATABASE
不能在事务块内执行。
因此,它不能直接在函数或DO
语句内运行,在这些情况下,它将隐式地位于事务块内。Postgres 11引入的SQL过程也无法解决此问题。
您可以通过有条件地执行DDL语句来在psql内解决它:
SELECT 'CREATE DATABASE mydb'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec
\gexec
发送当前查询缓冲区到服务器,然后将查询输出(如果有)的每行的每个列视为要执行的SQL语句。
使用 \gexec
您只需要调用一次 psql :
echo "SELECT 'CREATE DATABASE mydb' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec" | psql
您的连接可能需要更多的psql选项,如角色、端口、密码等,请参考:
不能使用psql -c "SELECT ...\gexec"
,因为\gexec
是一个psql元命令,而-c
选项需要一个单独的命令,关于此官方文档有说明:
command
必须是一个完全可被服务器解析的命令字符串(即不包含任何psql特定的功能),或者是一个单独的反斜杠命令。因此,在-c
选项中不能混合SQL和psql元命令。
您可以使用一个dblink
连接返回到当前数据库,它运行在事务块之外。因此,效果也不能被回滚。
安装附加模块dblink(每个数据库只需安装一次):
接下来:
DO
$do$
BEGIN
IF EXISTS (SELECT FROM pg_database WHERE datname = 'mydb') THEN
RAISE NOTICE 'Database already exists'; -- optional
ELSE
PERFORM dblink_exec('dbname=' || current_database() -- current db
, 'CREATE DATABASE mydb');
END IF;
END
$do$;
同样地,连接时您可能需要更多的psql选项。请参阅Ortwin添加的答案:
关于dblink的详细说明:
您可以将此作为函数进行重复使用。
如果您想要一个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"
-c = run command in database session, command is given in string
-t = skip header and footer
-q = silent mode for grep
|| = logical OR, if grep fails to find match run the subsequent command
c:\Program Files\PostgreSQL\9.6\bin $ psql.exe -U admin -tc "SELECT 1 FROM pg_database WHERE datname = 'my_db'" | grep -q 1 || psql -U admin -c "CREATE DATABASE my_db" 'grep' 不是内部或外部命令,也不是可运行的程序或批处理文件。
我做错了什么? - Anton Anikeevgrep
。在Windows上,默认情况下未安装 grep
。您可以搜索 gnu grep windows
以查找可在Windows上使用的版本。 - Rod-X
和 -A
选项以获得更一致的结果。然后,您可以将其值写入变量中,并使用更易读的 if 语句 dbExists=\
psql -U postgres -X -A -t -c ...`和
if [ -z "$dbExists" ]; then... else... fi`。 - Nux如果你不关心数据,你可以先删除数据库,然后重新创建它:
DROP DATABASE IF EXISTS dbname;
CREATE DATABASE dbname;
PostgreSQL不支持在CREATE DATABASE
语句中使用IF NOT EXISTS
。它仅适用于CREATE SCHEMA
。此外,CREATE DATABASE
不能在事务中执行,因此不能在DO
块中与异常捕获一起使用。
当发出CREATE SCHEMA IF NOT EXISTS
并且模式已经存在时,会引发带有重复对象信息的通知(而不是错误)。
要解决这些问题,您需要使用dblink
扩展,它打开到数据库服务器的新连接,并执行查询而不进入事务。您可以重新使用提供空字符串的连接参数。
下面是PL/pgSQL
代码,它完全模拟具有与CREATE SCHEMA IF NOT EXISTS
相同行为的CREATE DATABASE IF NOT EXISTS
。它通过dblink
调用CREATE DATABASE
,捕获duplicate_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
$$;
这个解决方案没有像其他答案那样存在竞争条件,即在检查数据库是否存在和创建自身之间,可能会由外部进程(或相同脚本的其他实例)创建数据库。
此外,当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
我不得不使用略微扩展的版本,与 @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$
我必须启用 dblink
扩展,并提供 dblink 的凭据。
适用于 Postgres 9.4。
如果您能使用shell,请尝试
psql -U postgres -c 'select 1' -d $DB &>dev/null || psql -U postgres -tc 'create database $DB'
我认为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'"
最好的方法就是直接运行SQL。
CREATE DATABASE MY_DATABASE;
如果数据库已经存在,则会抛出“数据库已存在错误”,你可以根据需要进行处理,否则它将创建该数据库。我不认为它会在你的数据库上创建一个新的数据库。 :D
如果使用psql运行,还有另一种方式
psql --quiet -d postgres -c "CREATE DATABASE $DB_DATABASE;" || :
ERROR: database "" already exists
,但可以忽略。该提示与程序无关。在阅读了所有这些我认为是对于缺乏 PostgreSQL 用户创建的 IF NOT EXIST 选项的可怕解决方案的复杂解决办法之后,我几乎忘记了只需在 shell 级别处理它的简单方式。尽管这可能不是某些人想要的,但我认为很多人都希望简单而不是创建过程和复杂结构。
我正在使用 Docker,在这里是从我的 Bash 脚本中加载数据到开发环境的重要片段:
execute_psql_command_pipe () {
$DOCKER_COMMAND exec -it $POSTGRES_CONTAINER bash -c "echo \"$1\"| psql -h localhost -U postgres || echo psql command failed - object likely exists"
}
read -r -d '' CREATE_USER_COMMANDS << EOM
CREATE DATABASE MY_DATABASE;
create user User1 WITH PASSWORD 'password';
create user User2 WITH PASSWORD 'password';
EOM
execute_psql_command_pipe "$CREATE_USER_COMMANDS"
有一些问题,但这是我能找到的最简单的方法来实现我想要的:在脚本的第一遍创建,在现有的第二遍继续执行。 顺便说一句,回显输出不会显示,但命令会继续执行,因为回显命令以0退出。
对于任何命令(如创建数据库),也可以采取同样的方法。 显然,对于可能发生的其他错误,它可能失败(或成功,取决于观点),但你可以获取psql的输出打印机以便添加更多处理。
[根据评论中指出的更新,添加了创建数据库命令]
createdb $DATABASE 2> /dev/null || echo "database already exists"
如果你期望的错误不是 "数据库"x"已存在" 这种情况,显然这样做行不通(例如,权限被拒绝)。无论如何,如果这是一个关注点,我们总可以在这之前进行相应的检查。
不要忘记设置 DATABASE
的值,并传递所需的开关给 createdb
命令。最好还可以像这样操作:
export PGHOST=localhost
export PGUSER=user
export PGPASSWORD=p455w0rd
...
createdb -U <user> <dbname>
。 - Daniel Katz
\gexec
,但它起作用了。 - FilBot3