PostgreSQL สร้างตารางหากไม่มีอยู่


175

ในสคริปต์ MySQL คุณสามารถเขียน:

CREATE TABLE IF NOT EXISTS foo ...;

... อย่างอื่น ...

และจากนั้นคุณสามารถเรียกใช้สคริปต์ได้หลายครั้งโดยไม่ต้องสร้างตารางขึ้นใหม่

คุณจะทำสิ่งนี้ใน PostgreSQL ได้อย่างไร

คำตอบ:


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();เพราะคุณอาจต้องการโทรหลายครั้ง
Erwin Brandstetter

1
Brandstetter: ฉันเห็นด้วยกับคุณ ปัญหาที่ฉันพบคือฉันไม่รู้ว่าฟังก์ชั่นนั้นถูกสร้างขึ้นมาหรือไม่ (เช่นเดียวกับตารางอาจมีหรือไม่มีอยู่) ดังนั้นฉันต้องการให้แน่ใจว่าฟังก์ชั่นถูกสร้างขึ้นก่อนที่ฉันจะเรียก
ไม่มีที่สิ้นสุด

83

ลองสิ่งนี้:

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

นี่เป็นทางออกที่สะอาดกว่า ควร upvoted
SDReyes

4
ในความเป็นจริงฉันกลัวว่าจะมีคำตอบกี่คำเกี่ยวกับ 'ฟังก์ชั่น'
SDReyes

8
@SDReyes โซลูชั่นอื่น ๆ เหล่านั้นถูกโพสต์ก่อน Postgres 9.1 ซึ่งรวมถึงIF NOT EXISTSตัวเลือก
กริช

2
ไม่แน่ใจว่าคำตอบนี้สนับสนุน @ @ erwin-brandstetter อย่างไร
ร่วมประชุม

@ ผู้ที่ถูกต้องคนนี้จะแสดงให้เห็นว่าการใช้พารามิเตอร์คือคำตอบที่นำฉันไม่เห็นว่าจนกว่าฉันจะเห็นมัน วิธีนี้ช่วยได้บ้าง
Angry 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);');

มันอาจจะง่ายขึ้นอีกที่จะใช้เพียงหนึ่งพารามิเตอร์ถ้าจะแยกชื่อตารางออกจากพารามิเตอร์แบบสอบถาม นอกจากนี้ฉันออกจาก schemas


3

โซลูชันนี้ค่อนข้างคล้ายกับคำตอบของ Erwin Brandstetter แต่ใช้ภาษา sql เท่านั้น

การติดตั้ง PostgreSQL ไม่ทั้งหมดมีภาษา plpqsql โดยค่าเริ่มต้นซึ่งหมายความว่าคุณอาจต้องโทรCREATE LANGUAGE plpgsqlก่อนที่จะสร้างฟังก์ชั่นและหลังจากนั้นต้องลบภาษาอีกครั้งเพื่อออกจากฐานข้อมูลในสถานะเดียวกับที่เคยเป็นมาก่อน (แต่ถ้าฐานข้อมูลเท่านั้น ไม่มีภาษา plpgsql ที่จะเริ่มต้นด้วย) ดูว่าความซับซ้อนเพิ่มขึ้นอย่างไร

การเพิ่ม plpgsql อาจไม่เป็นปัญหาหากคุณใช้งานสคริปต์ในเครื่องอย่างไรก็ตามหากใช้สคริปต์เพื่อตั้งค่าสคีมาที่ลูกค้าอาจไม่ต้องการออกจากการเปลี่ยนแปลงเช่นนี้ในฐานข้อมูลลูกค้า

การแก้ปัญหานี้เป็นแรงบันดาลใจโพสต์โดย 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();

โซลูชันของคุณยอดเยี่ยมแม้เมื่อมี plpgsql ขยายได้ง่ายสำหรับการสร้างมุมมองและฟังก์ชั่นบนวัตถุที่ไม่มีอยู่ ณ เวลานั้น เช่นมุมมองบนตารางจากเซิร์ฟเวอร์ต่างประเทศ คุณบันทึกวันของฉัน! ขอบคุณ!
Alex Yu

3

ไม่มี CREATE TABLE ถ้าไม่มี EXISTS ... แต่คุณสามารถเขียนขั้นตอนง่าย ๆ ได้เช่น:

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

ไม่มี CREATE TABLE ถ้าไม่มี EXISTS ... แต่คุณสามารถเขียนขั้นตอนง่าย ๆ ได้เช่น:

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.