PostgreSQL tworzy tabelę, jeśli nie istnieje

174

W skrypcie MySQL możesz napisać:

CREATE TABLE IF NOT EXISTS foo ...;

... inne rzeczy ...

a następnie możesz uruchamiać skrypt wiele razy bez ponownego tworzenia tabeli.

Jak to robisz w PostgreSQL?

peter2108
źródło

Odpowiedzi:

278

Ta funkcja została zaimplementowana w Postgres 9.1 :

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



W przypadku starszych wersji jest funkcja umożliwiająca obejście tego problemu:

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;

Połączenie:

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

Uwagi:

  • W kolumnach schemanamei tablenamein pg_tablesrozróżniana jest wielkość liter. W przypadku podwójnego cudzysłowu identyfikatory w CREATE TABLEinstrukcji należy użyć dokładnie tej samej pisowni. Jeśli tego nie zrobisz, musisz użyć małych liter. Widzieć:

  • pg_tableszawiera tylko rzeczywiste tabele . Identyfikator może nadal być zajęty przez powiązane obiekty. Widzieć:

  • Jeśli rola wykonująca tę funkcję nie ma uprawnień niezbędnych do utworzenia tabeli, możesz zechcieć użyć SECURITY DEFINERtej funkcji i uczynić ją własnością innej roli z niezbędnymi uprawnieniami. Ta wersja jest wystarczająco bezpieczna.

Erwin Brandstetter
źródło
Jestem zmuszony korzystać z istniejącej bazy danych Postgres 8.4. Ten hack załatwia sprawę, dziękuję!
Bezgraniczny
1
@Boundless: Widziałem, że Twoja zmiana została odrzucona jako „zbyt drobna”. Zastosowałem to, bo nie będzie bolało. Jednak powinieneś wykonać CREATE FUNCTIONtylko raz. Chodzi o to, SELECT create_mytable();że możesz chcieć dzwonić wiele razy.
Erwin Brandstetter
1
Brandstetter: Zgadzam się z tobą. Problem, na który natknąłem się, polegał na tym, że nie wiedziałem, czy funkcja została utworzona, czy nie (podobnie jak tabela może istnieć lub nie). Dlatego chcę się upewnić, że funkcja została utworzona, zanim ją wywołam.
Bezgraniczna
82

Spróbuj tego:

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
źródło
to jest faktycznie czystsze rozwiązanie. powinno być przegłosowane.
SDReyes
4
w rzeczywistości jestem przerażony, ile jest rozwiązań związanych z „funkcją”.
SDReyes
8
@SDReyes te inne rozwiązania zostały opublikowane przed Postgres 9.1, który zawierał IF NOT EXISTSopcję.
Kris
2
Nie jestem pewien, jak ta odpowiedź przyczyniła się do tego, że odpowiedź @ erwin-brandstetter była kompletna sama w sobie.
comiventor
@comiventor poprawne, ten jednak pokazuje, że użycie parametru jest. Wiodąca odpowiedź Nie zauważyłem tego, dopóki nie zobaczyłem tego. To trochę pomaga.
Angry 84
7

Stworzyłem ogólne rozwiązanie z istniejących odpowiedzi, które można ponownie wykorzystać dla dowolnej tabeli:

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;

Stosowanie:

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

Można by jeszcze bardziej uprościć, aby pobrać tylko jeden parametr, jeśli wyodrębni się nazwę tabeli z parametru zapytania. Pominąłem też schematy.

Ingo Fischer
źródło
2

To rozwiązanie jest nieco podobne do odpowiedzi Erwina Brandstettera, ale używa tylko języka sql.

Nie wszystkie instalacje PostgreSQL mają domyślnie język plpqsql, co oznacza, że ​​może być konieczne wywołanie CREATE LANGUAGE plpgsqlprzed utworzeniem funkcji, a następnie ponowne usunięcie języka, aby pozostawić bazę danych w tym samym stanie, w jakim była wcześniej (ale tylko jeśli baza danych nie ma języka plpgsql na początku). Widzisz, jak rośnie złożoność?

Dodanie pliku plpgsql może nie stanowić problemu, jeśli uruchamiasz swój skrypt lokalnie, jednak jeśli skrypt jest używany do konfigurowania schematu u klienta, pozostawienie takich zmian w bazie danych klientów może być niepożądane.

To rozwiązanie zostało zainspirowane postem Andreasa Scherbauma .

-- 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
źródło
Twoje rozwiązanie jest świetne, nawet jeśli dostępny jest plik plpgsql. Można go łatwo rozszerzyć w celu tworzenia widoków i funkcji na obiektach nieistniejących w danym momencie. Np. Widoki tabel z zagranicznych serwerów. Uratowałeś mi dzień! Dzięki!
Alex Yu
2

Nie ma opcji CREATE TABLE, JEŚLI NIE ISTNIEJE ... ale możesz napisać prostą procedurę, na przykład:

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
źródło
2

Nie ma opcji CREATE TABLE, JEŚLI NIE ISTNIEJE ... ale możesz napisać prostą procedurę, na przykład:

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
źródło
wewnątrz wyzwalacza nie zawsze działa: gist.github.com/igilfanov/4df5e90d8a88d653132746a223639f45 BŁĄD: relacja „foo” już istnieje
igilfanov