Restrições
Você pode perguntar ao catálogo do sistema pg_database
- acessível a partir de qualquer banco de dados no mesmo cluster de banco de dados. A parte complicada é que CREATE DATABASE
só pode ser executado como uma única instrução. O manual:
CREATE DATABASE
não pode ser executado dentro de um bloco de transação.
Portanto, ele não pode ser executado diretamente dentro de uma função ou DO
instrução, onde estaria implicitamente dentro de um bloco de transação.
(Os procedimentos SQL, introduzidos no Postgres 11, também não podem ajudar nisso .)
Solução alternativa de dentro do psql
Você pode contornar isso de dentro do psql executando a instrução DDL condicionalmente:
SELECT 'CREATE DATABASE mydb'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec
O manual:
\gexec
Envia o buffer de consulta atual para o servidor e, a seguir, trata cada coluna de cada linha da saída da consulta (se houver) como uma instrução SQL a ser executada.
Solução alternativa do shell
Com \gexec
você só precisa chamar o psql uma vez :
echo "SELECT 'CREATE DATABASE mydb' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec" | psql
Você pode precisar de mais opções do psql para sua conexão; função, porta, senha, ... Veja:
O mesmo não pode ser chamado, psql -c "SELECT ...\gexec"
pois \gexec
é um meta-comando psql e a -c
opção espera um único comando para o qual o manual indica:
command
deve ser uma string de comando completamente analisável pelo servidor (ou seja, não contém recursos específicos do psql) ou um único comando de barra invertida. Portanto, você não pode misturar meta-comandos SQL e psql dentro de uma -c
opção.
Solução alternativa de dentro da transação Postgres
Você pode usar uma dblink
conexão de volta ao banco de dados atual, que funciona fora do bloco de transação. Portanto, os efeitos também não podem ser revertidos.
Instale o módulo adicional dblink para isso (uma vez por banco de dados):
Então:
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$;
Novamente, você pode precisar de mais opções do psql para a conexão. Veja a resposta adicionada de Ortwin:
Explicação detalhada para dblink:
Você pode tornar esta função para uso repetido.
dblink_connect
.\gexec
quando executei a primeira consulta do shell, mas funcionou.outra alternativa, apenas no caso de você querer ter um script de shell que cria o banco de dados se ele não existir e, caso contrário, apenas o mantenha como está:
Achei isso útil em scripts de provisionamento devops, que você pode querer executar várias vezes na mesma instância.
fonte
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.
O que eu fiz errado ?grep
em seu caminho. No Windows,grep
não é instalado por padrão. Você pode pesquisar paragnu grep windows
encontrar uma versão que funcione no Windows.Tive que usar uma versão ligeiramente estendida que @Erwin Brandstetter usou:
Tive que habilitar a
dblink
extensão, além de fornecer as credenciais para dblink. Funciona com Postgres 9.4.fonte
Se você não se importa com os dados, pode primeiro descartar o banco de dados e depois recriá-lo:
fonte
O PostgreSQL não apoiar
IF NOT EXISTS
paraCREATE DATABASE
declaração. É compatível apenas comCREATE SCHEMA
. Além disso,CREATE DATABASE
não pode ser emitido na transação, portanto, não pode ser emDO
bloco com captura de exceção.Quando
CREATE SCHEMA IF NOT EXISTS
é emitido e o esquema já existe, é gerado um aviso (não erro) com informações de objeto duplicadas.Para resolver esses problemas, você precisa usar a
dblink
extensão que abre uma nova conexão com o servidor de banco de dados e executa a consulta sem entrar em transação. Você pode reutilizar os parâmetros de conexão fornecendo uma string vazia.Abaixo está o
PL/pgSQL
código que simula totalmente oCREATE DATABASE IF NOT EXISTS
mesmo comportamento doCREATE SCHEMA IF NOT EXISTS
. Ele chamaCREATE DATABASE
viadblink
, catchduplicate_database
exception (que é emitido quando o banco de dados já existe) e o converte em aviso com propagaçãoerrcode
. A mensagem de string foi anexada, skipping
da mesma maneira que elaCREATE SCHEMA IF NOT EXISTS
.Esta solução é sem nenhuma condição de corrida como nas outras respostas, onde o banco de dados pode ser criado por processo externo (ou outra instância do mesmo script) entre a verificação da existência do banco de dados e sua própria criação.
Além disso, quando
CREATE DATABASE
falha com outro erro que não o banco de dados já existe, esse erro é propagado como erro e não descartado silenciosamente. Só existe captura para oduplicate_database
erro. Portanto, realmente se comporta comoIF NOT EXISTS
deveria.Você pode colocar este código em sua própria função, chamá-lo diretamente ou a partir da transação. Apenas a reversão (restauração do banco de dados eliminado) não funcionaria.
Saída de teste (chamada duas vezes via DO e depois diretamente):
fonte
Se você pode usar shell, tente
Eu acho que
psql -U postgres -c "select 1" -d $DB
é mais fácil do queSELECT 1 FROM pg_database WHERE datname = 'my_db'
, e só preciso de um tipo de citação, mais fácil de combinarsh -c
.Eu uso isso na minha tarefa ansible
fonte
Basta criar o banco de dados usando a
createdb
ferramenta CLI:Se o banco de dados existir, ele retornará um erro:
fonte
Atualize para PostgreSQL 9.5 ou superior. Se (não) existir foi introduzido na versão 9.5.
fonte
if not exists
paraCREATE DATABASE
- nem mesmo no Postgres 11 postgresql.org/docs/current/static/sql-createdatabase.html