生成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;(编辑:鹰潭站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! | 

