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;