PostgreSQL-Erstelltabelle, falls nicht vorhanden

172

In einem MySQL-Skript können Sie schreiben:

CREATE TABLE IF NOT EXISTS foo ...;

... andere Sachen ...

Anschließend können Sie das Skript mehrmals ausführen, ohne die Tabelle neu zu erstellen.

Wie macht man das in PostgreSQL?

peter2108
quelle

Antworten:

276

Diese Funktion wurde in Postgres 9.1 implementiert :

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



Für ältere Versionen gibt es eine Funktion, um dies zu umgehen:

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;

Anruf:

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

Anmerkungen:

  • Bei den Spalten schemanameund tablenamein pg_tableswird zwischen Groß- und Kleinschreibung unterschieden. Wenn Sie Bezeichner in der CREATE TABLEAnweisung in doppelte Anführungszeichen setzen , müssen Sie genau dieselbe Schreibweise verwenden. Wenn Sie dies nicht tun, müssen Sie Zeichenfolgen in Kleinbuchstaben verwenden. Sehen:

  • pg_tablesenthält nur aktuelle Tabellen . Die Kennung kann weiterhin von verwandten Objekten belegt sein. Sehen:

  • Wenn die Rolle der Ausführung dieser Funktion nicht über die erforderlichen Berechtigungen verfügen , um die Tabelle erstellen Sie verwenden möchten SECURITY DEFINERfür die Funktion und machen es im Besitz von einer anderen Rolle mit den notwendigen Berechtigungen. Diese Version ist sicher genug.

Erwin Brandstetter
quelle
Ich bin gezwungen, eine vorhandene Postgres 8.4-Datenbank zu verwenden. Dieser Hack macht den Trick, danke!
Grenzenlos
1
@Boundless: Ich habe gesehen, dass Ihre Bearbeitung als "zu geringfügig" abgelehnt wurde. Ich habe es angewendet, weil es nicht weh tut. Sie sollten das jedoch CREATE FUNCTIONnur einmal ausführen . Es ist , SELECT create_mytable();dass Sie oft anrufen möchten.
Erwin Brandstetter
1
Brandstetter: Da stimme ich Ihnen zu. Das Problem, auf das ich stieß, war, dass ich nicht wusste, ob die Funktion erstellt wurde oder nicht (genau wie die Tabelle möglicherweise existiert oder nicht). Ich möchte also sicherstellen, dass die Funktion erstellt wird, bevor ich sie aufrufe.
Grenzenlos
81

Versuche dies:

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
quelle
Dies ist eigentlich die sauberere Lösung. sollte positiv bewertet werden.
SDReyes
4
Tatsächlich habe ich Angst davor, wie viele Lösungen mit 'Funktion' es gibt.
SDReyes
8
@SDReyes Diese anderen Lösungen wurden vor Postgres 9.1 veröffentlicht, das die IF NOT EXISTSOption enthielt .
Kris
2
Ich bin mir nicht sicher, wie diese Antwort dazu beigetragen hat, dass die Antwort von @ erwin-brandstetter an sich vollständig war.
Comiventor
@comiventor richtig, dieser zeigt jedoch, dass die Parameterverwendung ist. Die führende Antwort habe ich erst entdeckt, als ich diese gesehen habe. Das hilft ein bisschen.
Wütend 84
7

Ich habe aus den vorhandenen Antworten eine generische Lösung erstellt, die für jede Tabelle wiederverwendet werden kann:

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;

Verwendung:

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

Es könnte weiter vereinfacht werden, nur einen Parameter zu verwenden, wenn man den Tabellennamen aus dem Abfrageparameter extrahieren würde. Auch ich habe die Schemata weggelassen.

Ingo Fischer
quelle
2

Diese Lösung ähnelt der Antwort von Erwin Brandstetter, verwendet jedoch nur die SQL-Sprache.

Nicht alle PostgreSQL-Installationen haben standardmäßig die Sprache plpqsql. Dies bedeutet, dass Sie möglicherweise CREATE LANGUAGE plpgsqlvor dem Erstellen der Funktion aufrufen und anschließend die Sprache erneut entfernen müssen, um die Datenbank im gleichen Zustand wie zuvor zu belassen (jedoch nur, wenn die Datenbank vorhanden ist) hatte zunächst nicht die Sprache plpgsql). Sehen Sie, wie die Komplexität wächst?

Das Hinzufügen von plpgsql ist möglicherweise kein Problem, wenn Sie Ihr Skript lokal ausführen. Wenn das Skript jedoch zum Einrichten eines Schemas bei einem Kunden verwendet wird, ist es möglicherweise nicht wünschenswert, solche Änderungen in der Kundendatenbank zu belassen.

Diese Lösung ist inspiriert von einem Beitrag von 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
quelle
Ihre Lösung ist großartig, auch wenn plpgsql verfügbar ist. Es ist einfach erweiterbar für die Erstellung von Ansichten und Funktionen für Objekte, die zum Zeitpunkt nicht vorhanden sind. ZB Ansichten auf Tabellen von fremden Servern. Du hast meinen Tag gerettet! Vielen Dank!
Alex Yu
2

Es gibt keine CREATE TABLE, WENN NICHT EXISTIERT ... aber Sie können eine einfache Prozedur dafür schreiben, so etwas wie:

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
quelle
2

Es gibt keine CREATE TABLE, WENN NICHT EXISTIERT ... aber Sie können eine einfache Prozedur dafür schreiben, so etwas wie:

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
quelle
innerhalb eines Triggers funktioniert nicht immer: gist.github.com/igilfanov/4df5e90d8a88d653132746a223639f45 FEHLER : Beziehung "foo" existiert bereits
igilfanov