我正在创建一个函数,它将id列添加到给定的表中,创建一个序列并填充新的列值。问题是列已创建,但现在我需要用创建序列的nextval()
填充它(1,2,3,4,5 ...)。我不知道如何在添加列句中指定它。
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 :(得分:2)
您的功能遇到了许多系列问题。请改用:
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;
如果您在同一ALTER TABLE
声明中设置列默认值,则会自动插入值 。请注意,这会对大表的性能产生很大影响,因为每行都必须更新,而添加NULL列只需要对系统目录进行微小的更改。
您必须定义架构才能创建对象。如果您要默认使用当前架构,则仍需在查询中考虑这一点。目录(或信息模式)表。表名仅与模式名称结合使用时唯一
我使用session information functions current_schema()
来查找当前架构。
在将动态SQL与用户输入一起使用时,必须防范 SQL注入。详细信息:
Table name as a PostgreSQL function parameter
如果序列已存在,请不要使用它!您可能会干扰现有对象。
通常情况下,不需要EXECUTE GRANT ALL ON TABLE ... TO postgres
。如果postgres
是超级用户(默认),则该角色无论如何都拥有所有权限。您可能想要postgres
所有者。这会有所作为。
我在两个查询中使用系统目录,而您在其中一个查询中使用信息架构。我通常不是信息模式的粉丝。它的膨胀视图是慢。所提供的信息遵循跨数据库标准,但是在编写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;