2009-11-19 19 views
81

Trong một kịch bản MySQL bạn có thể viết:PostgreSQL tạo bảng nếu không tồn tại

CREATE TABLE IF NOT EXISTS foo ...; 

... những thứ khác ...

và sau đó bạn có thể chạy các kịch bản nhiều lần mà không tái tạo bàn.

Bạn làm điều này như thế nào trong PostgreSQL?

+0

@ErwinBrandstetter: Điều này có thể đang khôi phục một chuỗi rất cũ không có mục đích, nhưng liên kết mà bạn đã cung cấp trỏ tới cùng một trang này. –

Trả lời

1

Không có CREATE TABLE IF NOT EXISTS ... nhưng bạn có thể viết một thủ tục đơn giản cho rằng, một cái gì đó như:

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

Đó là một chút lạ, nhưng chỉ đơn giản là có thể

+0

bên trong trình kích hoạt không phải lúc nào cũng hoạt động: https://gist.github.com/igilfanov/4df5e90d8a88d653132746a223639f45 LRI: quan hệ "foo" đã tồn tại – igilfanov

117

này tính năng này đã được implemented in Postgres 9.1:

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



Đối cũ phiên bản, đây là một chức năng để làm việc xung quanh nó:

CREATE OR REPLACE FUNCTION create_mytable() 
    RETURNS void AS 
$func$ 
BEGIN 
    IF EXISTS (SELECT 1 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; 

Gọi:

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

Nếu người dùng không có đặc quyền cần thiết để tạo bảng có lẽ bạn muốn sử dụng SECURITY DEFINER. Phiên bản này đủ an toàn.

+0

Tôi đang bị buộc phải sử dụng cơ sở dữ liệu hiện có của postgres 8.4. Hack này làm các trick, cảm ơn bạn! – Boundless

+0

@ Vô biên: Tôi thấy rằng chỉnh sửa của bạn đã bị từ chối là "quá nhỏ". Tôi áp dụng nó, bởi vì nó sẽ không đau. Tuy nhiên, bạn chỉ nên thực thi 'CREATE FUNCTION' một lần. Đó là 'SELECT create_mytable();' mà bạn có thể muốn gọi nhiều lần. –

+0

Brandstetter: Tôi đồng ý với bạn. Vấn đề mà tôi gặp phải là tôi không biết liệu hàm đó có được tạo ra hay không (giống như bảng có thể có hoặc không tồn tại). Vì vậy, tôi muốn đảm bảo chức năng được tạo trước khi tôi gọi nó. – Boundless

5

Tôi tạo ra một giải pháp chung chung ra các câu trả lời hiện tại mà có thể được tái sử dụng cho bất kỳ bảng:

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; 

Cách sử dụng:

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

Nó có thể được đơn giản hóa hơn nữa để lấy chỉ là một tham số nếu một sẽ trích xuất tên bảng ra khỏi tham số truy vấn. Ngoài ra tôi còn lại các lược đồ. Cảm thấy tự do để mở rộng giải pháp của tôi nếu bạn biết làm thế nào để làm điều đó - Tôi không phải là sâu vào plpgsql nào (đây là lần đầu tiên tôi đối phó với nó).

1

Giải pháp này hơi giống với câu trả lời của Erwin Brandstetter, nhưng chỉ sử dụng ngôn ngữ sql. Không phải tất cả các cài đặt PostgreSQL đều có ngôn ngữ plpqsql theo mặc định, điều này có nghĩa là bạn có thể phải gọi CREATE LANGUAGE plpgsql trước khi tạo hàm, và sau đó phải loại bỏ ngôn ngữ một lần nữa, để cơ sở dữ liệu ở trạng thái giống như trước đây (nhưng chỉ khi cơ sở dữ liệu không có ngôn ngữ plpgsql để bắt đầu). Xem mức độ phức tạp tăng lên như thế nào?

Việc thêm plpgsql có thể không có vấn đề nếu bạn đang chạy tập lệnh cục bộ, tuy nhiên, nếu tập lệnh được sử dụng để thiết lập lược đồ tại một khách hàng, có thể không muốn thay đổi như thế này trong cơ sở dữ liệu khách hàng.

Giải pháp này được lấy cảm hứng từ a post by 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(); 
23

Hãy thử điều này:

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 (user_id) 
) 
+0

đây thực sự là giải pháp sạch hơn. nên được upvoted. – SDReyes

+2

trong thực tế, tôi đang sợ hãi của bao nhiêu giải pháp liên quan đến 'chức năng' có. – SDReyes

+4

@SDReyes các giải pháp khác đã được đăng trước Postgres 9.1 bao gồm tùy chọn 'NẾU KHÔNG tồn tại'. – Kris

1

Không có CREATE TABLE IF NOT EXISTS ... nhưng bạn có thể viết một thủ tục đơn giản cho rằng, một cái gì đó như:

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;