是否可以通过脚本/工具使用Oracle PL/SQL根据表fk关系自动生成多个delete语句?
在示例中:我有一个表: first (CHICKEN_CODE NUMBER),有30个表具有对其CHICKEN_CODE的fk引用,我需要删除这些表;还有其他150个表与这30个表有外键链接,我需要先删除它们。
有没有我可以运行的工具/脚本PL/SQL,以便根据FK关系为我生成所有必要的delete语句?
(顺便说一下,我知道关系上的级联删除,但请注意:我不能在我的生产数据库中使用它,因为它很危险!)
我正在使用Oracle DataBase 10G R2。
请注意这一点:
Generate Delete Statement From Foreign Key Relationships in SQL 2008?
另一个用户刚刚用SQL SERVER 2008编写了它,有谁能够转换到Oracle 10G PL/SQL?我不能...:-(
请假设V_CHICKEN和V_NATION是从根表中选择要删除的鸡的条件:条件是:根表上的"where COD_CHICKEN = V_CHICKEN和COD_NATION = V_NATION“。
发布于 2010-04-23 04:00:41
(我的第一个答案变得太长,很难编辑,它得到了社区维基,这真的很烦人。以下是该脚本的最新版本。)
此脚本尝试通过递归执行级联删除。当有循环引用时,它应该避免无限循环。但它要求所有循环引用约束都具有ON DELETE SET NULL或ON DELETE CASCADE。
CREATE OR REPLACE PROCEDURE delete_cascade(
table_owner VARCHAR2,
parent_table VARCHAR2,
where_clause VARCHAR2
) IS
/* Example call: execute delete_cascade('MY_SCHEMA', 'MY_MASTER', 'where ID=1'); */
child_cons VARCHAR2(30);
parent_cons VARCHAR2(30);
child_table VARCHAR2(30);
child_cols VARCHAR(500);
parent_cols VARCHAR(500);
delete_command VARCHAR(10000);
new_where_clause VARCHAR2(10000);
/* gets the foreign key constraints on other tables which depend on columns in parent_table */
CURSOR cons_cursor IS
SELECT owner, constraint_name, r_constraint_name, table_name, delete_rule
FROM all_constraints
WHERE constraint_type = 'R'
AND delete_rule = 'NO ACTION'
AND r_constraint_name IN (SELECT constraint_name
FROM all_constraints
WHERE constraint_type IN ('P', 'U')
AND table_name = parent_table
AND owner = table_owner)
AND NOT table_name = parent_table; -- ignore self-referencing constraints
/* for the current constraint, gets the child columns and corresponding parent columns */
CURSOR columns_cursor IS
SELECT cc1.column_name AS child_col, cc2.column_name AS parent_col
FROM all_cons_columns cc1, all_cons_columns cc2
WHERE cc1.constraint_name = child_cons
AND cc1.table_name = child_table
AND cc2.constraint_name = parent_cons
AND cc1.position = cc2.position
ORDER BY cc1.position;
BEGIN
/* loops through all the constraints which refer back to parent_table */
FOR cons IN cons_cursor LOOP
child_cons := cons.constraint_name;
parent_cons := cons.r_constraint_name;
child_table := cons.table_name;
child_cols := '';
parent_cols := '';
/* loops through the child/parent column pairs, building the column lists of the DELETE statement */
FOR cols IN columns_cursor LOOP
IF child_cols IS NULL THEN
child_cols := cols.child_col;
ELSE
child_cols := child_cols || ', ' || cols.child_col;
END IF;
IF parent_cols IS NULL THEN
parent_cols := cols.parent_col;
ELSE
parent_cols := parent_cols || ', ' || cols.parent_col;
END IF;
END LOOP;
/* construct the WHERE clause of the delete statement, including a subquery to get the related parent rows */
new_where_clause :=
'where (' || child_cols || ') in (select ' || parent_cols || ' from ' || table_owner || '.' || parent_table ||
' ' || where_clause || ')';
delete_cascade(cons.owner, child_table, new_where_clause);
END LOOP;
/* construct the delete statement for the current table */
delete_command := 'delete from ' || table_owner || '.' || parent_table || ' ' || where_clause;
-- this just prints the delete command
DBMS_OUTPUT.put_line(delete_command || ';');
-- uncomment if you want to actually execute it:
--EXECUTE IMMEDIATE delete_command;
-- remember to issue a COMMIT (not included here, for safety)
END;发布于 2010-04-21 07:50:44
问题是,如果顶级键列没有一直传播到底部。如果你可以从parent_id = :1的孙子文件中删除,那就好了。如果你非做不可,
DELETE FROM grandchild
WHERE child_id in (SELECT id FROM child WHERE parent_id = :1)然后,深入6到7层将给出难看的(并且可能很慢的)查询。
虽然你说你不能使约束级联,但你能让它们在初始时立即延迟吗?这样就不会影响现有的代码。您的“删除”会话将使所有约束被推迟。然后从父记录中删除,从子记录不在父记录中的子项中删除,从子记录中没有匹配的子项中删除,等等。
发布于 2010-04-21 02:44:18
这是开发您的PL/SQL技能和一般Oracle知识的一个很好的练习!
您需要标识所有表中具有从主表降级的关系的所有受约束的列。您可以从两个视图获取所需的所有信息:ALL_CONSTRAINTS和ALL_CONS_COLUMNS。(如果所有表都与执行脚本的用户位于相同的模式中,则可以根据需要使用USER_CONSTRAINTS和USER_CONS_COLUMNS )
此查询将查找引用回给定表的所有外键约束(本例中为CUSTOMER):
SELECT constraint_name, table_name, constraint_type
FROM all_constraints
WHERE constraint_type = 'R'
AND r_constraint_name IN (SELECT constraint_name
FROM all_constraints
WHERE constraint_type IN ('P', 'U')
AND table_name = 'CUSTOMER');
CONSTRAINT_NAME C
------------------------------ -
CUSTOMER_FK1 R
CUSTOMER_FK4 R
CUSTOMER_FK5 R
CUSTOMER_FK3 R
CUSTOMER_FK2 R现在,对于该查询的每个结果,您可以使用CONSTRAINT_NAME列来获取表和列名,您可以使用它们来编写DELETE语句,以删除所有子表中的所有子行。
此示例获取名为CUSTOMER_FK1的约束的表名和列名
SELECT table_name, column_name
FROM user_cons_columns
WHERE constraint_name = 'CUSTOMER_FK1'
TABLE_NAME COLUMN_NAME
----------------------------- ------------------------------------
RESERVATION CUSTOMER_UID所以你可以这样做,例如:
DELETE FROM reservation
WHERE customer_uid = 00153464或
DELETE FROM reservation
WHERE customer_uid IN (SELECT customer_uid
FROM customer
WHERE customer_type = 'X')但是您的子表也有子表,所以您当然必须首先删除那些子行(称为子行)。假设有一个名为reservation_detail的表,它与保留具有外键关系,reservation_detail的delete命令可能如下所示:
DELETE FROM reservation_detail
WHERE reservation_uid in (SELECT reservation_uid
FROM reservation
WHERE customer_uid IN (SELECT customer_uid
FROM customer
WHERE customer_type = 'X')如果reservation_detail也有孩子..。你明白了吧。当然,您可以使用连接而不是嵌套查询,但原理是相同的:依赖关系的层次越深,delete命令就变得越复杂。
所以现在你知道如何做了,挑战是编写一个通用的PL/SQL脚本来删除所有子行、孙行、曾子行……(ad infinitum)对于任何给定表,从下到上。您将不得不使用recursion。写起来应该是一个有趣的程序!
(最后一次编辑:删除了脚本;有关最终解决方案,请参阅我的另一个答案。)
https://stackoverflow.com/questions/2677081
复制相似问题