生成SQL以更新主键
|
副标题[/!--empirenews.page--]
我想更改主键和引用此值的所有表行. # table master master_id|name =============== foo|bar # table detail detail_id|master_id|name ======================== 1234|foo|blu 如果我给出一个脚本或功能 table=master,value-old=foo,value-new=abc 我想创建一个SQL片段,在所有引用表“master”的表上执行更新: update detail set master_id=value-new where master_id=value-new; ..... 在内省的帮助下,这应该是可能的. 我用postgres. 更新 问题是,有许多表具有表“master”的外键.我想要一种方法来自动更新所有具有外键到主表的表. 解决方法如果您需要更改PK,可以使用DEFFERED CONSTRAINTS:
数据准备: CREATE TABLE master(master_id VARCHAR(10) PRIMARY KEY,name VARCHAR(10));
INSERT INTO master(master_id,name) VALUES ('foo','bar');
CREATE TABLE detail(detail_id INT PRIMARY KEY,master_id VARCHAR(10),name VARCHAR(10),CONSTRAINT fk_det_mas FOREIGN KEY (master_id) REFERENCES master(master_id));
INSERT INTO detail(detail_id,master_id,name) VALUES (1234,'foo','blu');
在正常情况下,如果您尝试更改主细节,最终会出现错误: update detail set master_id='foo2' where master_id='foo'; -- ERROR: insert or update on table "detail" violates foreign key -- constraint "fk_det_mas" -- DETAIL: Key (master_id)=(foo2) is not present in table "master" update master set master_id='foo2' where master_id='foo'; -- ERROR: update or delete on table "master" violates foreign key -- constraint "fk_det_mas" on table "detail" -- DETAIL: Key (master_id)=(foo) is still referenced from table "detail". 但是,如果你将FK分辨率改为deffer,则没有问题: ALTER TABLE detail DROP CONSTRAINT fk_det_mas ; ALTER TABLE detail ADD CONSTRAINT fk_det_mas FOREIGN KEY (master_id) REFERENCES master(master_id) DEFERRABLE; BEGIN TRANSACTION; SET CONSTRAINTS ALL DEFERRED; UPDATE master set master_id='foo2' where master_id = 'foo'; UPDATE detail set master_id='foo2' where master_id = 'foo'; COMMIT; DBFiddle Demo 请注意,您可以在事务中执行许多操作,但在COMMIT期间,所有参照完整性检查都必须保留. 编辑 如果要自动执行此过程,可以使用动态SQL和元数据表.这里有一个FK专栏的概念证明: CREATE TABLE master(master_id VARCHAR(10) PRIMARY KEY,name)
VALUES ('foo',CONSTRAINT fk_det_mas FOREIGN KEY (master_id)
REFERENCES master(master_id)DEFERRABLE ) ;
INSERT INTO detail(detail_id,'blu');
CREATE TABLE detail_second(detail_id INT PRIMARY KEY,master_id_second_name VARCHAR(10),CONSTRAINT fk_det_mas_2 FOREIGN KEY (master_id_second_name)
REFERENCES master(master_id)DEFERRABLE ) ;
INSERT INTO detail_second(detail_id,master_id_second_name,name)
VALUES (1234,'blu');
和代码: BEGIN TRANSACTION;
SET CONSTRAINTS ALL DEFERRED;
DO $$
DECLARE
old_pk TEXT = 'foo';
new_pk TEXT = 'foo2';
table_name TEXT = 'master';
BEGIN
-- update childs
EXECUTE (select
string_agg(FORMAT('UPDATE %s SET %s = ''%s'' WHERE %s =''%s'' ;',c.relname,pa.attname,new_pk,old_pk),CHR(13)) AS sql
from pg_constraint pc
join pg_class c on pc.conrelid = c.oid
join pg_attribute pa ON pc.conkey[1] = pa.attnum
and pa.attrelid = pc.conrelid
join pg_attribute pa2 ON pc.confkey[1] = pa2.attnum
and pa2.attrelid = table_name::regclass
where pc.contype = 'f');
-- update parent
EXECUTE ( SELECT FORMAT('UPDATE %s SET %s = ''%s'' WHERE %s =''%s'';',old_pk)
FROM pg_constraint pc
join pg_class c on pc.conrelid = c.oid
join pg_attribute pa ON pc.conkey[1] = pa.attnum
and pa.attrelid = pc.conrelid
WHERE pc.contype IN ('p','u')
AND conrelid = table_name::regclass
);
END
$$;
COMMIT;
DBFiddle Demo 2 编辑2:
是的,我试过了.只需查看上面的现场演示链接. >之前的价值观 请确保将FK定义为DEFFERED. DBFiddle 2 with debug info 最后编辑
如果您只想获取SQL代码,则可以创建函数: CREATE FUNCTION generate_update_sql(table_name VARCHAR(100),old_pk VARCHAR(100),new_pk VARCHAR(100))
RETURNS TEXT
AS
$$
BEGIN
RETURN
-- update childs
(SELECT
string_agg(FORMAT('UPDATE %s SET %s = ''%s'' WHERE %s =''%s'' ;',CHR(13)) AS sql
FROM pg_constraint pc
JOIN pg_class c on pc.conrelid = c.oid
JOIN pg_attribute pa ON pc.conkey[1] = pa.attnum and pa.attrelid = pc.conrelid
JOIN pg_attribute pa2 ON pc.confkey[1] = pa2.attnum and pa2.attrelid = table_name::regclass
WHERE pc.contype = 'f') || CHR(13) ||
-- update parent
(SELECT FORMAT('UPDATE %s SET %s = ''%s'' WHERE %s =''%s'';',old_pk)
FROM pg_constraint pc
JOIN pg_class c on pc.conrelid = c.oid
JOIN pg_attribute pa ON pc.conkey[1] = pa.attnum and pa.attrelid = pc.conrelid
WHERE pc.contype IN ('p','u')
AND conrelid = table_name::regclass)
;
END
$$LANGUAGE plpgsql;
(编辑:湖南网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


