PostgreSQL создать таблицу, если не существует

175

В сценарии MySQL вы можете написать:

CREATE TABLE IF NOT EXISTS foo ...;

... другие вещи ...

и затем вы можете запустить скрипт много раз, не создавая заново таблицу.

Как вы делаете это в PostgreSQL?

peter2108
источник

Ответы:

279

Эта функция была реализована в Postgres 9.1 :

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



Для более старых версий здесь есть функция, чтобы обойти это:

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;

Вызов:

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

Ноты:

  • Столбцы schemanameи tablenameв pg_tablesчувствительны к регистру. Если вы используете двойные кавычки идентификаторов в CREATE TABLEвыражении, вам нужно использовать точно такое же написание. Если вы этого не сделаете, вам нужно использовать строчные буквы. Видеть:

  • pg_tablesсодержит только актуальные таблицы . Идентификатор все еще может быть занят связанными объектами. Видеть:

  • Если роль, выполняющая эту функцию, не обладает необходимыми привилегиями для создания таблицы, которую вы можете использовать SECURITY DEFINERдля этой функции, и сделайте ее владельцем другой роли с необходимыми привилегиями. Эта версия достаточно безопасна.

Эрвин Брандштеттер
источник
Я вынужден использовать существующую базу данных Postgres 8.4. Этот взлом делает свое дело, спасибо!
безграничный
1
@ Безгранично: я видел, что ваше редактирование было отклонено как "слишком незначительное". Я применил это, потому что это не повредит. Тем не менее, вы должны выполнить CREATE FUNCTIONтолько один раз. Это то, SELECT create_mytable();что вы можете позвонить много раз.
Эрвин Брандштеттер
1
Brandstetter: я согласен с вами. Проблема, с которой я столкнулся, заключалась в том, что я не знал, была ли создана функция или нет (как таблица может существовать или не существовать). Поэтому я хочу убедиться, что функция создана, прежде чем я ее вызову.
безграничный
84

Попробуй это:

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)
)
Ахиллес Рам Накиреканти
источник
это на самом деле более чистое решение. должно быть проголосовано
SDReyes
4
на самом деле, я в ужасе от того, сколько существует решений, включающих «функцию».
SDReyes
8
@SDReyes эти другие решения были опубликованы до Postgres 9.1, которая включала эту IF NOT EXISTSопцию.
Крис
2
Не уверен, как этот ответ помог, поскольку ответ @ erwin-brandstetter был сам по себе полным.
комивентор
@comiventor правильно, этот действительно показывает, как используется параметр. Главный ответ, который я не заметил, пока не увидел этот. Это немного помогает.
Злой 84
8

Я создал общее решение из существующих ответов, которые можно использовать для любой таблицы:

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;

Использование:

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

В дальнейшем можно было бы упростить использование только одного параметра, если вы извлечете имя таблицы из параметра запроса. Также я не учел схемы.

Инго Фишер
источник
3

Это решение несколько похоже на ответ Эрвина Брандштеттера, но использует только язык sql.

Не во всех установках PostgreSQL по умолчанию используется язык plpqsql, это означает, что вам, возможно, придется вызвать CREATE LANGUAGE plpgsqlперед созданием функции, а затем снова удалить язык, чтобы оставить базу данных в том же состоянии, в каком она была раньше (но только если база данных не было языка plpgsql для начала). Видите, как растет сложность?

Добавление plpgsql может не вызывать проблем, если вы выполняете свой сценарий локально, однако, если сценарий используется для настройки схемы у клиента, может быть нежелательно оставлять подобные изменения в базе данных клиентов.

Это решение вдохновлено постом Андреаса Шербаума .

-- 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
источник
Ваше решение отлично, даже когда plpgsql доступен. Он легко расширяется для создания представлений и функций на объектах, не существующих на данный момент. Например, просмотр таблиц с чужих серверов. Вы спасли мой день! Спасибо!
Алекс Ю
3

CREATE TABLE, ЕСЛИ НЕ СУЩЕСТВУЕТ ... нет, но вы можете написать для этого простую процедуру, например:

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
источник
3

CREATE TABLE, ЕСЛИ НЕ СУЩЕСТВУЕТ ... нет, но вы можете написать для этого простую процедуру, например:

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'
  );
Шимон Липиньски
источник
внутри триггера не всегда работает: gist.github.com/igilfanov/4df5e90d8a88d653132746a223639f45 ОШИБКА: отношение "foo" уже существует
igilfanov