PostgreSQL cria tabela se não existir

175

Em um script MySQL, você pode escrever:

CREATE TABLE IF NOT EXISTS foo ...;

... outras coisas ...

e então você pode executar o script várias vezes sem recriar a tabela.

Como você faz isso no PostgreSQL?

peter2108
fonte

Respostas:

279

Este recurso foi implementado no Postgres 9.1 :

CREATE TABLE IF NOT EXISTS myschema.mytable (i integer);



Para versões mais antigas , aqui está uma função para contornar isso:

CREATE OR REPLACE FUNCTION create_mytable ()
  RETURNS void AS
$func$
BEGIN
   IF EXISTS (SELECT FROM pg_catalog.pg_tables 
              WHERE  schemaname = 'myschema'
              AND    tablename  = 'mytable') THEN
      RAISE NOTICE 'Table myschema.mytable already exists.';
   ELSE
      CREATE TABLE myschema.mytable (i integer);
   END IF;
END
$func$ LANGUAGE plpgsql;

Ligar:

SELECT create_mytable();        -- call as many times as you want. 

Notas:

  • As colunas schemanamee tablenamein pg_tablesdiferenciam maiúsculas de minúsculas. Se você CREATE TABLEcolocar aspas nos identificadores na declaração, precisará usar exatamente a mesma ortografia. Caso contrário, você precisará usar cadeias de letras minúsculas. Vejo:

  • pg_tablescontém apenas tabelas reais . O identificador ainda pode estar ocupado por objetos relacionados. Vejo:

  • Se a função que executa esta função não tiver os privilégios necessários para criar a tabela que você deseja usar SECURITY DEFINERpara a função e torná-la propriedade de outra função com os privilégios necessários. Esta versão é segura o suficiente.

Erwin Brandstetter
fonte
Estou sendo forçado a usar um banco de dados existente do postgres 8.4. Esse truque faz o truque, obrigado!
Boundless
1
@ Sem limites: vi que sua edição foi rejeitada como "muito pequena". Eu apliquei, porque não vai doer. No entanto, você deve executar a CREATE FUNCTIONúnica vez. Talvez SELECT create_mytable();você queira ligar várias vezes.
Erwin Brandstetter
1
Brandstetter: Eu concordo com você. O problema que encontrei foi que não sabia se a função foi criada ou não (assim como a tabela pode ou não existir). Então, quero garantir que a função seja criada antes de chamá-la.
Boundless
84

Tente o seguinte:

CREATE TABLE IF NOT EXISTS app_user (
  username varchar(45) NOT NULL,
  password varchar(450) NOT NULL,
  enabled integer NOT NULL DEFAULT '1',
  PRIMARY KEY (username)
)
Achilles Ram Nakirekanti
fonte
Esta é realmente a solução mais limpa. deve ser votado.
SDReyes
4
de fato, tenho pavor de quantas soluções envolvem 'função' existem.
SDReyes
8
@SDReyes essas outras soluções foram postadas antes do Postgres 9.1, que incluía a IF NOT EXISTSopção.
Kris
2
Não sei como essa resposta contribuiu, pois a resposta @ erwin-brandstetter estava completa em si mesma.
Comiventor
@comiventor correto, este faz como sempre mostra que o uso do parâmetro é. Isso ajuda um pouco.
irritada 84
8

Criei uma solução genérica a partir das respostas existentes que podem ser reutilizadas para qualquer tabela:

CREATE OR REPLACE FUNCTION create_if_not_exists (table_name text, create_stmt text)
RETURNS text AS
$_$
BEGIN

IF EXISTS (
    SELECT *
    FROM   pg_catalog.pg_tables 
    WHERE    tablename  = table_name
    ) THEN
   RETURN 'TABLE ' || '''' || table_name || '''' || ' ALREADY EXISTS';
ELSE
   EXECUTE create_stmt;
   RETURN 'CREATED';
END IF;

END;
$_$ LANGUAGE plpgsql;

Uso:

select create_if_not_exists('my_table', 'CREATE TABLE my_table (id integer NOT NULL);');

Poderia ser simplificado ainda mais o uso de apenas um parâmetro se alguém extraísse o nome da tabela do parâmetro de consulta. Também deixei de fora os esquemas.

Ingo Fischer
fonte
3

Essa solução é um pouco semelhante à resposta de Erwin Brandstetter, mas usa apenas a linguagem sql.

Nem todas as instalações do PostgreSQL possuem a linguagem plpqsql por padrão; isso significa que você pode precisar chamar CREATE LANGUAGE plpgsqlantes de criar a função e depois remover a linguagem novamente, para deixar o banco de dados no mesmo estado em que estava antes (mas somente se o banco de dados não tinha o idioma plpgsql para começar). Veja como a complexidade cresce?

Adicionar o plpgsql pode não ser um problema se você estiver executando seu script localmente; no entanto, se o script for usado para configurar o esquema em um cliente, pode não ser desejável deixar alterações como essa no banco de dados de clientes.

Esta solução é inspirada em um post de Andreas Scherbaum .

-- Function which creates table
CREATE OR REPLACE FUNCTION create_table () RETURNS TEXT AS $$
    CREATE TABLE table_name (
       i int
    );
    SELECT 'extended_recycle_bin created'::TEXT;
    $$
LANGUAGE 'sql';

-- Test if table exists, and if not create it
SELECT CASE WHEN (SELECT true::BOOLEAN
    FROM   pg_catalog.pg_tables 
    WHERE  schemaname = 'public'
    AND    tablename  = 'table_name'
  ) THEN (SELECT 'success'::TEXT)
  ELSE (SELECT create_table())
END;

-- Drop function
DROP FUNCTION create_table();
zpon
fonte
Sua solução é ótima, mesmo quando o plpgsql está disponível. É fácil de expandir para a criação de visualizações e funções em objetos que não existem no ponto do tempo. Por exemplo, visualizações em tabelas de servidores estrangeiros. Você salvou o meu dia! Obrigado!
Alex Yu
3

Não há CREATE TABLE SE NÃO EXISTE ... mas você pode escrever um procedimento simples para isso, algo como:

CREATE OR REPLACE FUNCTION prc_create_sch_foo_table() RETURNS VOID AS $$
BEGIN

EXECUTE 'CREATE TABLE /* IF NOT EXISTS add for PostgreSQL 9.1+ */ sch.foo (
                    id serial NOT NULL, 
                    demo_column varchar NOT NULL, 
                    demo_column2 varchar NOT NULL,
                    CONSTRAINT pk_sch_foo PRIMARY KEY (id));
                   CREATE INDEX /* IF NOT EXISTS add for PostgreSQL 9.5+ */ idx_sch_foo_demo_column ON sch.foo(demo_column);
                   CREATE INDEX /* IF NOT EXISTS add for PostgreSQL 9.5+ */ idx_sch_foo_demo_column2 ON sch.foo(demo_column2);'
               WHERE NOT EXISTS(SELECT * FROM information_schema.tables 
                        WHERE table_schema = 'sch' 
                            AND table_name = 'foo');

         EXCEPTION WHEN null_value_not_allowed THEN
           WHEN duplicate_table THEN
           WHEN others THEN RAISE EXCEPTION '% %', SQLSTATE, SQLERRM;

END; $$ LANGUAGE plpgsql;
igilfanov
fonte
3

Não há CREATE TABLE SE NÃO EXISTE ... mas você pode escrever um procedimento simples para isso, algo como:

CREATE OR REPLACE FUNCTION execute(TEXT) RETURNS VOID AS $$
BEGIN
  EXECUTE $1;
END; $$ LANGUAGE plpgsql;


SELECT 
  execute($$
      CREATE TABLE sch.foo 
      (
        i integer
      )
  $$) 
WHERE 
  NOT exists 
  (
    SELECT * 
    FROM information_schema.tables 
    WHERE table_name = 'foo'
      AND table_schema = 'sch'
  );
Szymon Lipiński
fonte
dentro de um gatilho nem sempre funciona: gist.github.com/igilfanov/4df5e90d8a88d653132746a223639f45 ERROR: relation "foo" já existe
igilfanov