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 DATABASE
solo 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 DO
declaració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 \gexec
solo 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 \gexec
es un metacomando psql y la -c
opción espera un solo comando para el cual el manual dice:
command
debe 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 -c
opció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
.\gexec
cuando 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 ?grep
en tu camino. En Windows,grep
no se instala de forma predeterminada. Puede buscar paragnu grep windows
encontrar una versión que funcione en Windows.Tuve que usar una versión ligeramente extendida que usó @Erwin Brandstetter:
Tuve que habilitar la
dblink
extensió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 EXISTS
para laCREATE DATABASE
declaración. Solo se admite enCREATE SCHEMA
. AdemásCREATE DATABASE
, no se puede emitir en transacción, por lo que no puedeDO
bloque con excepción de captura.Cuando
CREATE SCHEMA IF NOT EXISTS
se 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
dblink
extensió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/pgSQL
código que simula completamenteCREATE DATABASE IF NOT EXISTS
con el mismo comportamiento que enCREATE SCHEMA IF NOT EXISTS
. Llama aCREATE DATABASE
través de la excepción dedblink
capturaduplicate_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, skipping
de 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 DATABASE
falla 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_database
error. Entonces realmente se comporta comoIF NOT EXISTS
deberí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 $DB
es 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
createdb
herramienta 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 exists
paraCREATE DATABASE
- ni siquiera en Postgres 11 postgresql.org/docs/current/static/sql-createdatabase.html