生成SQL以更新主键

前端之家收集整理的这篇文章主要介绍了生成SQL以更新主键前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我想更改主键和引用此值的所有表行.
# 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

SET CONSTRAINTS sets the behavior of constraint checking within the current transaction. IMMEDIATE constraints are checked at the end of each statement. DEFERRED constraints are not checked until transaction commit. Each constraint has its own IMMEDIATE or DEFERRED mode.

数据准备:

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:

I tried it,but it does not work. It would be nice,if the script could show the sql. This is enough. After looking at the generated sql I can execute it if psql -f

have you tried it? It did not work for me.

是的,我试过了.只需查看上面的现场演示链接.
我用更多的调试信息准备相同的演示:

>之前的价值观
>执行sql
>之后的价值观

请确保将FK定义为DEFFERED.

@L_404_3@

最后编辑

Then I wanted to see the sql instead of executing it. I removed “perform” from your fiddle,but then I get an error. See: 07004

如果您只想获取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;

执行:

SELECT generate_update_sql('master','foo');

UPDATE detail SET master_id = 'foo' WHERE master_id ='foo' ;
UPDATE detail_second SET master_id_second_name = 'foo' 
 WHERE master_id_second_name ='foo' ; 
UPDATE master SET master_id = 'foo' WHERE master_id ='foo';

DBFiddle Function Demo

当然,还有一个需要改进的地方,例如处理标识符,例如“名称中包含空格的表格”等.

猜你在找的MsSQL相关文章