1

我正在做一個函數,它將ID列添加到給定的表,創建一個序列並填充新的列值。問題在於該列已創建,但現在我需要使用創建的序列(1,2,3,4,5 ...)的nextval()填充它。我不知道如何在添加列句中指定。改變表添加列的默認值,並執行每行的默認值

CREATE OR REPLACE FUNCTION create_id(tabla character varying) 
    RETURNS void AS 
$BODY$ 
DECLARE 

BEGIN 

IF NOT EXISTS (SELECT information_schema.columns.column_name FROM information_schema.columns WHERE information_schema.columns.table_name=tabla AND information_schema.columns.column_name='id') 
THEN 
    EXECUTE 'ALTER TABLE '|| tabla ||' ADD COLUMN id numeric(8,0)'; 

    IF NOT EXISTS (SELECT relname FROM pg_class WHERE relname='seq_id_'||tabla) 
    THEN 
     EXECUTE 'CREATE SEQUENCE seq_id_'||tabla||' INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1'; 
     EXECUTE 'GRANT ALL ON TABLE seq_id_'||tabla||' TO postgres'; 
     EXECUTE 'ALTER TABLE ONLY '||tabla||' ALTER COLUMN id SET DEFAULT nextval(''seq_id_'||tabla||'''::regclass)'; 
    END IF; 
END IF; 

RETURN; 
END; 
$BODY$ 
    LANGUAGE plpgsql; 
+0

您需要更新新的'id'柱,一次用nextval加油吧?按什麼標準排序? – keltar

+0

按無標準排序,第一個記錄的第一個值,第二個記錄的第二個值,第三個記錄的第三個值.. – Egidi

+1

如果沒有排序條件,則沒有「第一個」和「第二個」字...無序的選擇可能會返回值以任何順序它更喜歡。 – keltar

回答

2

你的功能從衆多的一系列問題困擾。而不是使用這樣的:如果設置在同一ALTER TABLE聲明的列默認

CREATE OR REPLACE FUNCTION f_create_id(_tbl text) 
    RETURNS void AS 
$func$ 
DECLARE 
    _seq text := _tbl || '_id_seq'; 
BEGIN 

IF EXISTS (
    SELECT 1 FROM pg_namespace n 
    JOIN pg_class  c ON c.relnamespace = n.oid 
    JOIN pg_attribute a ON a.attrelid = c.oid 
    WHERE n.nspname = current_schema() -- default to current schema 
    AND c.relname = _tbl 
    AND a.attname = 'id' 
    AND NOT a.attisdropped) 
THEN 
    RAISE EXCEPTION 'Column already exists!'; RETURN; 
END IF; 

IF EXISTS (
    SELECT 1 FROM pg_namespace n 
    JOIN pg_class  c ON c.relnamespace = n.oid 
    WHERE n.nspname = current_schema() -- default to current schema 
    AND c.relname = _seq) 
THEN 
    RAISE EXCEPTION 'Sequence already exists!'; RETURN; 
END IF; 

EXECUTE format('CREATE SEQUENCE %I.%I', current_schema(), _seq;  
EXECUTE format($$ALTER TABLE %I.%I ADD COLUMN id numeric(8,0) 
       DEFAULT nextval('%I'::regclass)$$ -- one statement! 
       , current_schema(), _tbl, _seq); 

END 
$func$ LANGUAGE plpgsql; 

要點

  • ,值會自動插入。請注意,這對於大表的性能有很大的影響,因爲每行都必須更新,而添加NULL列只需要對系統目錄稍作更改。

  • 必須定義模式來創建對象。如果你想默認爲當前模式,你還必須考慮這在你的查詢目錄(或信息模式)表。表名稱只與模式名稱結合使用。我使用session information functions current_schema()找出當前模式。

  • 當使用動態SQL和用戶輸入時,您必須防範SQL注入。詳情:
    Table name as a PostgreSQL function parameter

  • 如果序列已經存在,請不要使用它!您可能會干擾現有對象。

  • 通常情況下,你做不是需要EXECUTE GRANT ALL ON TABLE ... TO postgres。如果postgres是超級用戶(默認),則該角色仍具有所有權限。您可能想讓postgres所有者。這會有所作爲。

  • 我在兩個查詢中都使用了系統目錄,而在其中一個查詢中使用了信息架構。我一般不信息schema.Its的粉絲臃腫的意見是。所提供的信息遵循交叉數據庫標準,但是在編寫plpgsql函數時有什麼好處呢,無論如何都是100%不可移植的?

高級替代

  • 我建議使用列名id,這是一個SQL反模式。改爲使用適當的描述性名稱,如tablename || '_id'

  • 使用什麼要點numeric(8,0)?如果您不想使用小數位,爲什麼不使用integer?更簡單,更小,更快。

鑑於這種情況,你是一個serial type好得多,讓一切更簡單:

CREATE OR REPLACE FUNCTION f_create_id(_tbl text) 
    RETURNS void AS 
$func$ 
BEGIN 

IF EXISTS (
    SELECT 1 FROM pg_namespace n 
    JOIN pg_class  c ON c.relnamespace = n.oid 
    JOIN pg_attribute a ON a.attrelid = c.oid 
    WHERE n.nspname = current_schema() -- default to current schema 
    AND c.relname = _tbl 
    AND a.attname = _tbl || '_id'  -- proper column name 
    AND NOT a.attisdropped) 
THEN 
    RAISE EXCEPTION 'Column already exists!'; 
ELSE 
    EXECUTE format('ALTER TABLE %I.%I ADD COLUMN %I serial' 
       , current_schema(), _tbl, _tbl || '_id'); 
END IF; 

END 
$func$ LANGUAGE plpgsql;