如何在函数的 UPDATE 或 SELECT 语句中使用动态列名?
How to use dynamic column names in an UPDATE or SELECT statement in a function?
在 PostgreSQL 9.1 中,PL/pgSQL,给定一个查询:
select fk_list.relname from ...
其中 relname
属于 name
类型(例如 "table_name")。
如何获得可直接在 UPDATE
语句中使用的 "relname" 的适当值:
Update <relname> set ...
在 PL/pgSQL 脚本中?
将quote_ident(r.relname)
用作:
Update quote_ident(r.relname) Set ...
失败:
syntax error at or near "("
LINE 55: UPDATE quote_ident(r.relname) ....
我正在使用的完整代码:
CREATE FUNCTION merge_children_of_icd9 (ocicd9 text,
ocdesc text, ncicd9 text, ncdesc text)
RETURNS void AS $BODY$
DECLARE
r RECORD;
BEGIN
FOR r IN
WITH fk_actions ( code, action ) AS (
VALUES ('a', 'error'),
('r', 'restrict'),
('c', 'cascade'),
('n', 'set null'),
('d', 'set default')
), fk_list AS (
SELECT pg_constraint.oid AS fkoid, conrelid, confrelid::regclass AS parentid,
conname, relname, nspname,
fk_actions_update.action AS update_action,
fk_actions_delete.action AS delete_action,
conkey AS key_cols
FROM pg_constraint
JOIN pg_class ON conrelid = pg_class.oid
JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
JOIN fk_actions AS fk_actions_update ON confupdtype = fk_actions_update.code
JOIN fk_actions AS fk_actions_delete ON confdeltype = fk_actions_delete.code
WHERE contype = 'f'
), fk_attributes AS (
SELECT fkoid, conrelid, attname, attnum
FROM fk_list
JOIN pg_attribute ON conrelid = attrelid AND attnum = ANY(key_cols)
ORDER BY fkoid, attnum
), fk_cols_list AS (
SELECT fkoid, array_agg(attname) AS cols_list
FROM fk_attributes
GROUP BY fkoid
)
SELECT fk_list.fkoid, fk_list.conrelid, fk_list.parentid, fk_list.conname,
fk_list.relname, fk_cols_list.cols_list
FROM fk_list
JOIN fk_cols_list USING (fkoid)
WHERE parentid = 'icd9'::regclass
LOOP
RAISE NOTICE 'now in loop. relname is %', quote_ident(r.relname);
RAISE NOTICE 'cols_list[1] is %', quote_ident(r.cols_list[1]);
RAISE NOTICE 'cols_list[2] is %', quote_ident(r.cols_list[2]);
RAISE NOTICE 'now doing update';
UPDATE quote_ident(r.relname) SET r.cols_list[1] = ncicd9, r.cols_list[2] = ncdesc
WHERE r.cols_list[1] = ocicd9 AND r.cols_list[2] = ocdesc;
RAISE NOTICE 'finished update';
END LOOP;
RETURN;
END $BODY$ LANGUAGE plpgsql VOLATILE;
-- select merge_children_of_icd9('', 'aodm type 2', '', 'aodm, type 2');
我敢肯定这种事情经常发生,但我似乎无法使用 PostgreSQL 找到类似的东西。有没有更好的方法?
在 PL/pgSQL 中的 UPDATE
语句中,table 名称必须作为文字给出。如果要动态设置 table 名称和列,应使用 EXECUTE
命令并将查询字符串粘贴在一起:
EXECUTE 'UPDATE ' || quote_ident(r.relname) ||
' SET ' || quote_ident(r.cols_list[1]) || ' = , ' ||
quote_ident(r.cols_list[2]) || ' = ' ||
' WHERE ' || quote_ident(r.cols_list[1]) || ' = AND ' ||
quote_ident(r.cols_list[2]) || ' = '
USING ncicd9, ncdesc, ocicd9, ocdesc;
USING
子句只能用于替换数据值,如上所示。
你需要动态 SQL 和 EXECUTE
就像@Patrick 已经提供的那样。
但是,您的函数和 EXECUTE
部分都可以 简单得多 。特别是,使用 format()
安全地连接更长的查询字符串(自 pg 9.1 起可用):
CREATE OR REPLACE FUNCTION merge_children_of_icd9 (_ocicd9 text, _ocdesc text
, _ncicd9 text, _ncdesc text)
RETURNS void AS
$func$
DECLARE
_sql text;
BEGIN
FOR _sql IN
SELECT format('UPDATE %3$s SET %1$I = , %2$I =
WHERE %1$I = AND %2$I = '
, x.cols[1], x.cols[2], x.conrelid::regclass::text)
FROM (
SELECT c.conrelid, array_agg(a.attname ORDER BY a.attnum) AS cols
FROM pg_constraint c
JOIN pg_attribute a ON a.attrelid = c.conrelid
AND a.attnum = ANY(c.conkey)
WHERE c.confrelid = 'icd9'::regclass
AND c.contype = 'f'
GROUP BY c.oid, c.conrelid
ORDER BY c.oid
) x
LOOP
-- RAISE NOTICE '%', _sql;
EXECUTE _sql
USING _ocicd9, _ocdesc, _ncicd9, _ncdesc;
END LOOP;
END
$func$ LANGUAGE plpgsql;
如果 FK 约束未跨越至少两列或列的数据类型与 text
不兼容,则函数会出错。可能符合预期,也可能不符合预期。
关于如何安全传递标识符和执行动态的详细信息SQL:
- Table name as a PostgreSQL function parameter
- Define table and column names as arguments in a plpgsql function?
在 PostgreSQL 9.1 中,PL/pgSQL,给定一个查询:
select fk_list.relname from ...
其中 relname
属于 name
类型(例如 "table_name")。
如何获得可直接在 UPDATE
语句中使用的 "relname" 的适当值:
Update <relname> set ...
在 PL/pgSQL 脚本中?
将quote_ident(r.relname)
用作:
Update quote_ident(r.relname) Set ...
失败:
syntax error at or near "(" LINE 55: UPDATE quote_ident(r.relname) ....
我正在使用的完整代码:
CREATE FUNCTION merge_children_of_icd9 (ocicd9 text,
ocdesc text, ncicd9 text, ncdesc text)
RETURNS void AS $BODY$
DECLARE
r RECORD;
BEGIN
FOR r IN
WITH fk_actions ( code, action ) AS (
VALUES ('a', 'error'),
('r', 'restrict'),
('c', 'cascade'),
('n', 'set null'),
('d', 'set default')
), fk_list AS (
SELECT pg_constraint.oid AS fkoid, conrelid, confrelid::regclass AS parentid,
conname, relname, nspname,
fk_actions_update.action AS update_action,
fk_actions_delete.action AS delete_action,
conkey AS key_cols
FROM pg_constraint
JOIN pg_class ON conrelid = pg_class.oid
JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
JOIN fk_actions AS fk_actions_update ON confupdtype = fk_actions_update.code
JOIN fk_actions AS fk_actions_delete ON confdeltype = fk_actions_delete.code
WHERE contype = 'f'
), fk_attributes AS (
SELECT fkoid, conrelid, attname, attnum
FROM fk_list
JOIN pg_attribute ON conrelid = attrelid AND attnum = ANY(key_cols)
ORDER BY fkoid, attnum
), fk_cols_list AS (
SELECT fkoid, array_agg(attname) AS cols_list
FROM fk_attributes
GROUP BY fkoid
)
SELECT fk_list.fkoid, fk_list.conrelid, fk_list.parentid, fk_list.conname,
fk_list.relname, fk_cols_list.cols_list
FROM fk_list
JOIN fk_cols_list USING (fkoid)
WHERE parentid = 'icd9'::regclass
LOOP
RAISE NOTICE 'now in loop. relname is %', quote_ident(r.relname);
RAISE NOTICE 'cols_list[1] is %', quote_ident(r.cols_list[1]);
RAISE NOTICE 'cols_list[2] is %', quote_ident(r.cols_list[2]);
RAISE NOTICE 'now doing update';
UPDATE quote_ident(r.relname) SET r.cols_list[1] = ncicd9, r.cols_list[2] = ncdesc
WHERE r.cols_list[1] = ocicd9 AND r.cols_list[2] = ocdesc;
RAISE NOTICE 'finished update';
END LOOP;
RETURN;
END $BODY$ LANGUAGE plpgsql VOLATILE;
-- select merge_children_of_icd9('', 'aodm type 2', '', 'aodm, type 2');
我敢肯定这种事情经常发生,但我似乎无法使用 PostgreSQL 找到类似的东西。有没有更好的方法?
在 PL/pgSQL 中的 UPDATE
语句中,table 名称必须作为文字给出。如果要动态设置 table 名称和列,应使用 EXECUTE
命令并将查询字符串粘贴在一起:
EXECUTE 'UPDATE ' || quote_ident(r.relname) ||
' SET ' || quote_ident(r.cols_list[1]) || ' = , ' ||
quote_ident(r.cols_list[2]) || ' = ' ||
' WHERE ' || quote_ident(r.cols_list[1]) || ' = AND ' ||
quote_ident(r.cols_list[2]) || ' = '
USING ncicd9, ncdesc, ocicd9, ocdesc;
USING
子句只能用于替换数据值,如上所示。
你需要动态 SQL 和 EXECUTE
就像@Patrick 已经提供的那样。
但是,您的函数和 EXECUTE
部分都可以 简单得多 。特别是,使用 format()
安全地连接更长的查询字符串(自 pg 9.1 起可用):
CREATE OR REPLACE FUNCTION merge_children_of_icd9 (_ocicd9 text, _ocdesc text
, _ncicd9 text, _ncdesc text)
RETURNS void AS
$func$
DECLARE
_sql text;
BEGIN
FOR _sql IN
SELECT format('UPDATE %3$s SET %1$I = , %2$I =
WHERE %1$I = AND %2$I = '
, x.cols[1], x.cols[2], x.conrelid::regclass::text)
FROM (
SELECT c.conrelid, array_agg(a.attname ORDER BY a.attnum) AS cols
FROM pg_constraint c
JOIN pg_attribute a ON a.attrelid = c.conrelid
AND a.attnum = ANY(c.conkey)
WHERE c.confrelid = 'icd9'::regclass
AND c.contype = 'f'
GROUP BY c.oid, c.conrelid
ORDER BY c.oid
) x
LOOP
-- RAISE NOTICE '%', _sql;
EXECUTE _sql
USING _ocicd9, _ocdesc, _ncicd9, _ncdesc;
END LOOP;
END
$func$ LANGUAGE plpgsql;
如果 FK 约束未跨越至少两列或列的数据类型与 text
不兼容,则函数会出错。可能符合预期,也可能不符合预期。
关于如何安全传递标识符和执行动态的详细信息SQL:
- Table name as a PostgreSQL function parameter
- Define table and column names as arguments in a plpgsql function?