Restricciones
Puede solicitar el catálogo del sistema pg_database, accesible desde cualquier base de datos en el mismo grupo de bases de datos. La parte complicada es que CREATE DATABASEsolo se puede ejecutar como una única declaración. El manual:
CREATE DATABASE no se puede ejecutar dentro de un bloque de transacciones.
Por lo tanto, no se puede ejecutar directamente dentro de una función o DOdeclaración, donde estaría implícitamente dentro de un bloque de transacción.
(Los procedimientos SQL, introducidos con Postgres 11, tampoco pueden ayudar con esto ).
Solución alternativa desde psql
Puede solucionarlo desde psql ejecutando la declaración DDL de forma condicional:
SELECT 'CREATE DATABASE mydb'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec
El manual:
\gexec
Envía el búfer de consulta actual al servidor, luego trata cada columna de cada fila de la salida de la consulta (si la hubiera) como una declaración SQL para ser ejecutada.
Solución alternativa desde el caparazón
Con \gexecsolo necesitas llamar a psql una vez :
echo "SELECT 'CREATE DATABASE mydb' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec" | psql
Es posible que necesite más opciones de psql para su conexión; rol, puerto, contraseña, ... Ver:
No se puede llamar al mismo, psql -c "SELECT ...\gexec"ya que \gexeces un metacomando psql y la -copción espera un solo comando para el cual el manual dice:
commanddebe ser una cadena de comando que el servidor pueda analizar completamente (es decir, que no contenga funciones específicas de psql) o un solo comando de barra invertida. Por lo tanto, no puede mezclar metacomandos SQL y psql dentro de una -copción.
Solución alternativa desde dentro de la transacción de Postgres
Podrías usar un dblink conexión de regreso a la base de datos actual, que se ejecuta fuera del bloque de transacciones. Por lo tanto, los efectos tampoco se pueden revertir.
Instale el módulo adicional dblink para esto (una vez por base de datos):
Luego:
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$;
Nuevamente, es posible que necesite más opciones de psql para la conexión. Vea la respuesta agregada de Ortwin:
Explicación detallada de dblink:
Puede convertir esta función en una función para uso repetido.
dblink_connect.\gexeccuando ejecuté la primera consulta desde el shell, pero funcionó.otra alternativa, en caso de que desee tener un script de shell que cree la base de datos si no existe y, de lo contrario, la mantiene como está:
Encontré que esto es útil en scripts de aprovisionamiento de devops, que es posible que desee ejecutar varias veces en la misma instancia.
fuente
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' is not recognized as an internal or external command, operable program or batch file.Qué hice mal ?grepen tu camino. En Windows,grepno se instala de forma predeterminada. Puede buscar paragnu grep windowsencontrar una versión que funcione en Windows.Tuve que usar una versión ligeramente extendida que usó @Erwin Brandstetter:
Tuve que habilitar la
dblinkextensión, además tuve que proporcionar las credenciales para dblink. Funciona con Postgres 9.4.fuente
Si no le importan los datos, puede eliminar la base de datos primero y luego volver a crearla:
fuente
PostgreSQL no es compatible
IF NOT EXISTSpara laCREATE DATABASEdeclaración. Solo se admite enCREATE SCHEMA. AdemásCREATE DATABASE, no se puede emitir en transacción, por lo que no puedeDObloque con excepción de captura.Cuando
CREATE SCHEMA IF NOT EXISTSse emite y el esquema ya existe, se genera un aviso (no un error) con información de objeto duplicada.Para resolver estos problemas, debe usar la
dblinkextensión que abre una nueva conexión al servidor de la base de datos y ejecutar la consulta sin ingresar a la transacción. Puede reutilizar los parámetros de conexión proporcionando una cadena vacía.A continuación se muestra el
PL/pgSQLcódigo que simula completamenteCREATE DATABASE IF NOT EXISTScon el mismo comportamiento que enCREATE SCHEMA IF NOT EXISTS. Llama aCREATE DATABASEtravés de la excepción dedblinkcapturaduplicate_database(que se emite cuando la base de datos ya existe) y la convierte en un aviso con propagaciónerrcode. El mensaje de cadena se ha agregado, skippingde la misma manera que lo haceCREATE SCHEMA IF NOT EXISTS.Esta solución no tiene ninguna condición de carrera como en otras respuestas, donde la base de datos puede ser creada por un proceso externo (u otra instancia del mismo script) entre verificar si la base de datos existe y su propia creación.
Además, cuando
CREATE DATABASEfalla con otro error que la base de datos ya existe, este error se propaga como error y no se descarta silenciosamente. Solo hay una trampa para elduplicate_databaseerror. Entonces realmente se comporta comoIF NOT EXISTSdebería.Puede poner este código en su propia función, llamarlo directamente o desde la transacción. Solo la reversión (restaurar la base de datos eliminada) no funcionaría.
Prueba de salida (llamada dos veces a través de DO y luego directamente):
fuente
Si puede usar shell, intente
Creo que
psql -U postgres -c "select 1" -d $DBes más fácilSELECT 1 FROM pg_database WHERE datname = 'my_db'y solo necesito un tipo de cotización, más fácil de combinar consh -c.Uso esto en mi tarea ansible
fuente
Simplemente cree la base de datos usando la
createdbherramienta CLI:Si la base de datos existe, devolverá un error:
fuente
Actualice a PostgreSQL 9.5 o superior. Si (no) existe se introdujo en la versión 9.5.
fuente
if not existsparaCREATE DATABASE- ni siquiera en Postgres 11 postgresql.org/docs/current/static/sql-createdatabase.html