PostgreSQL crea una tabella se non esiste


175

In uno script MySQL puoi scrivere:

CREATE TABLE IF NOT EXISTS foo ...;

... altre cose ...

e quindi è possibile eseguire lo script molte volte senza ricreare la tabella.

Come si fa in PostgreSQL?

Risposte:


279

Questa funzione è stata implementata in Postgres 9.1 :

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



Per le versioni precedenti , ecco una funzione per aggirare il problema:

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;

Chiamata:

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

Appunti:

  • Le colonne schemanamee tablenamein pg_tablesfanno distinzione tra maiuscole e minuscole. Se si citano due volte gli identificatori CREATE TABLEnell'istruzione, è necessario utilizzare lo stesso ortografia. In caso contrario, è necessario utilizzare stringhe minuscole. Vedere:

  • pg_tablescontiene solo tabelle effettive . L'identificatore può essere ancora occupato da oggetti correlati. Vedere:

  • Se il ruolo che esegue questa funzione non dispone dei privilegi necessari per creare la tabella, è possibile che si desideri utilizzare SECURITY DEFINERla funzione e renderla di proprietà di un altro ruolo con i privilegi necessari. Questa versione è abbastanza sicura.


Sono stato costretto a utilizzare un database postgres 8.4 esistente. Questo trucco fa il trucco, grazie!
Senza limiti

1
@Boundless: ho visto che la tua modifica è stata respinta come "troppo minore". L'ho applicato, perché non farà male. Tuttavia, è necessario eseguire l' CREATE FUNCTIONunica volta. È SELECT create_mytable();che potresti voler chiamare molte volte.
Erwin Brandstetter,

1
Brandstetter: sono d'accordo con te. Il problema che ho riscontrato è che non sapevo se la funzione fosse stata creata o meno (proprio come la tabella potrebbe o meno esistere). Quindi voglio assicurarmi che la funzione sia creata prima di chiamarla.
Senza limiti

84

Prova questo:

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)
)

questa è in realtà la soluzione più pulita. dovrebbe essere votato.
SDReyes,

4
in effetti, sono terrorizzato da quante soluzioni ci sono "funzioni".
SDReyes,

8
@SDReyes queste altre soluzioni sono state pubblicate prima di Postgres 9.1 che includeva l' IF NOT EXISTSopzione.
Kris,

2
Non sono sicuro di come questa risposta abbia contribuito alla risposta di @ erwin-brandstetter in sé.
comiventor

@comiventor corretto, questo fa come mai mostra che l'utilizzo dei parametri è. La risposta principale non l'ho notato fino a quando non ho visto questo. Questo aiuta un po '.
Angry 84

8

Ho creato una soluzione generica dalle risposte esistenti che può essere riutilizzata per qualsiasi tabella:

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);');

Potrebbe essere semplificato ulteriormente per prendere solo un parametro se si estrae il nome della tabella dal parametro della query. Inoltre ho lasciato fuori gli schemi.


3

Questa soluzione è in qualche modo simile alla risposta di Erwin Brandstetter, ma utilizza solo il linguaggio sql.

Non tutte le installazioni di PostgreSQL hanno il linguaggio plpqsql per impostazione predefinita, ciò significa che potrebbe essere necessario chiamare CREATE LANGUAGE plpgsqlprima di creare la funzione e successivamente rimuovere nuovamente la lingua, per lasciare il database nello stesso stato di prima (ma solo se il database non aveva il linguaggio plpgsql per cominciare). Vedi come cresce la complessità?

L'aggiunta di plpgsql potrebbe non essere un problema se si esegue lo script localmente, tuttavia, se lo script viene utilizzato per impostare lo schema in un cliente, potrebbe non essere desiderabile lasciare cambiamenti come questo nel database dei clienti.

Questa soluzione è ispirata a un post di 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();

La tua soluzione è eccezionale anche quando è disponibile plpgsql. È facilmente estensibile per la creazione di viste e funzioni su oggetti che non esistono al momento. Ad esempio viste su tabelle da server esterni. Mi hai salvato la giornata! Grazie!
Alex Yu,

3

Non esiste CREATE TABLE SE NON ESISTE ... ma puoi scrivere una semplice procedura per questo, qualcosa come:

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;

3

Non esiste CREATE TABLE SE NON ESISTE ... ma puoi scrivere una semplice procedura per questo, qualcosa come:

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'
  );

all'interno di un trigger non sempre funziona: gist.github.com/igilfanov/4df5e90d8a88d653132746a223639f45 ERRORE: la relazione "foo" esiste già
igilfanov
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.