PostgreSQL crée une table si elle n'existe pas

175

Dans un script MySQL, vous pouvez écrire:

CREATE TABLE IF NOT EXISTS foo ...;

... d'autres choses ...

et puis vous pouvez exécuter le script plusieurs fois sans recréer la table.

Comment faites-vous cela dans PostgreSQL?

peter2108
la source

Réponses:

279

Cette fonctionnalité a été implémentée dans Postgres 9.1 :

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



Pour les anciennes versions , voici une fonction pour contourner ce problème:

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;

Appel:

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

Remarques:

  • Les colonnes schemanameet tablenamein pg_tablessont sensibles à la casse. Si vous doublez les identifiants dans l' CREATE TABLEinstruction, vous devez utiliser exactement la même orthographe. Sinon, vous devez utiliser des chaînes en minuscules. Voir:

  • pg_tablesne contient que des tableaux réels . L'identifiant peut encore être occupé par des objets associés. Voir:

  • Si le rôle exécutant cette fonction ne dispose pas des privilèges nécessaires pour créer la table, vous souhaiterez peut-être l'utiliser SECURITY DEFINERpour la fonction et la faire appartenir à un autre rôle disposant des privilèges nécessaires. Cette version est suffisamment sûre.

Erwin Brandstetter
la source
Je suis obligé d'utiliser une base de données postgres 8.4 existante. Ce hack fait l'affaire, merci!
Boundless
1
@Boundless: j'ai vu que votre modification était rejetée comme "trop ​​mineure". Je l'ai appliqué, car cela ne fera pas de mal. Cependant, vous ne devez exécuter CREATE FUNCTIONqu'une seule fois. C'est SELECT create_mytable();que vous voudrez peut-être appeler plusieurs fois.
Erwin Brandstetter
1
Brandstetter: Je suis d'accord avec vous. Le problème que j'ai rencontré était que je ne savais pas si la fonction a été créée ou non (tout comme la table peut exister ou non). Je veux donc m'assurer que la fonction est créée avant de l'appeler.
Boundless
84

Essaye ça:

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)
)
Achille Ram Nakirekanti
la source
c'est en fait la solution la plus propre. devrait être voté pour.
SDReyes
4
en fait, je suis terrifié par le nombre de solutions impliquant la «fonction».
SDReyes
8
@SDReyes ces autres solutions ont été publiées avant Postgres 9.1 qui incluait l' IF NOT EXISTSoption.
Kris
2
Je ne sais pas comment cette réponse a contribué, car la réponse @ erwin-brandstetter était complète en soi.
comiventor le
@comiventor correct, celui-ci montre cependant que l'utilisation des paramètres est la réponse principale, je ne l'ai pas repérée avant de voir celle-ci. Cela aide un peu.
Angry 84
8

J'ai créé une solution générique à partir des réponses existantes qui peuvent être réutilisées pour n'importe quelle table:

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;

Usage:

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

Il pourrait être simplifié davantage de ne prendre qu'un seul paramètre si l'on extrayait le nom de la table du paramètre de requête. J'ai également omis les schémas.

Ingo Fischer
la source
3

Cette solution est quelque peu similaire à la réponse d'Erwin Brandstetter, mais n'utilise que le langage sql.

Toutes les installations PostgreSQL n'ont pas le langage plpqsql par défaut, cela signifie que vous devrez peut-être appeler CREATE LANGUAGE plpgsqlavant de créer la fonction, et ensuite supprimer à nouveau le langage, pour laisser la base de données dans le même état qu'avant (mais seulement si la base de données n'avait pas le langage plpgsql pour commencer). Voyez comment la complexité grandit?

L'ajout de plpgsql peut ne pas poser de problème si vous exécutez votre script localement, cependant, si le script est utilisé pour configurer le schéma chez un client, il peut ne pas être souhaitable de laisser des modifications comme celle-ci dans la base de données des clients.

Cette solution est inspirée d' un article d'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
la source
Votre solution est excellente même lorsque plpgsql est disponible. Il est facilement extensible pour la création de vues et de fonctions sur des objets qui n'existent pas à un moment donné. Par exemple, des vues sur des tables provenant de serveurs étrangers. Tu as sauvé ma journée! Merci!
Alex Yu le
3

Il n'y a pas de CREATE TABLE IF NOT EXISTS ... mais vous pouvez écrire une procédure simple pour cela, quelque chose comme:

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
la source
3

Il n'y a pas de CREATE TABLE IF NOT EXISTS ... mais vous pouvez écrire une procédure simple pour cela, quelque chose comme:

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
la source
à l'intérieur d'un déclencheur ne fonctionne pas toujours: gist.github.com/igilfanov/4df5e90d8a88d653132746a223639f45 ERREUR: la relation "foo" existe déjà
igilfanov