【发布时间】:2016-09-08 19:56:45
【问题描述】:
我很难找到一种简单的方法来删除父 ID 的所有子记录。子表也可能有自己的子表,因此我们需要删除层次结构中的所有记录。什么方法最简单?我可以手动转到每个子表并找到它的子表,然后创建一个脚本,但是表太多了,想知道一种更简单的方法。任何帮助表示赞赏!
【问题讨论】:
我很难找到一种简单的方法来删除父 ID 的所有子记录。子表也可能有自己的子表,因此我们需要删除层次结构中的所有记录。什么方法最简单?我可以手动转到每个子表并找到它的子表,然后创建一个脚本,但是表太多了,想知道一种更简单的方法。任何帮助表示赞赏!
【问题讨论】:
这几乎就是主键和外键以及像ON DELETE CASCADE 这样的子句的用途。如果还不算太晚,您可以在进行任何删除之前尝试添加 PK 和 FK 约束;那么一切都会变得简单。
添加:基于进一步讨论。下面的查询可用于查找父表的所有后代表。查询可能会在许多方面得到改进,但它可能是一个不错的起点。
with f as (
select constraint_name, table_name, r_constraint_name
from user_constraints
where constraint_type = 'R'
),
p as (
select constraint_name, table_name
from user_constraints
where constraint_type = 'P'
),
j (child_table, f_key, parent_table, p_key) as (
select f.table_name, f.constraint_name, p.table_name, f.r_constraint_name
from p join f on p.constraint_name = f.r_constraint_name
union all
select 'EMPLOYEES', (select constraint_name from p
where table_name = 'EMPLOYEES'), null, null from dual
)
select level as lvl, j.*
from j
start with parent_table is null
connect by nocycle parent_table = prior child_table
order by lvl, parent_table, child_table;
在这种情况下,“父”表是 EMPLOYEES,名称在同一行出现了两次。如果需要,可以将其制成绑定变量。我使用了 EMPLOYEES(注意:它必须全部大写,因为这是字符串值存储在系统表中的方式),因为我在标准 HR 模式上运行它;输出:
LVL CHILD_TABLE F_KEY PARENT_TABLE P_KEY
----- ----------------- -------------------- ----------------- -----------------
1 EMPLOYEES EMP_EMP_ID_PK
2 DEPARTMENTS DEPT_MGR_FK EMPLOYEES EMP_EMP_ID_PK
2 JOB_HISTORY JHIST_EMP_FK EMPLOYEES EMP_EMP_ID_PK
3 JOB_HISTORY JHIST_DEPT_FK DEPARTMENTS DEPT_ID_PK
【讨论】:
ON DELETE SET NULL,在某些情况下这是正确的设置。例如,如果一个员工表有 emp_id (PK) 和 mgr_id(FK 指向同一个表中的 emp_id),并且从表中删除了经理行,那么在几乎所有情况下,该经理的员工的 mgr_id 字段都应设置为null 直到进一步的指示(直到分配了新的经理);所有经理的直接下属的行不应该与经理的行一起从表中删除。
ON DELETE 选项中的either 是正确的做法。在我们的数据库中,我们有验证表,以确保在各个字段中仅使用“有效”值。我有许多引用这些表的外键,这些表故意没有指定ON DELETE 选项,而且我经常接到某人的电话,说:“嘿,我试图从 VAL_UMPTY_SQUAT 表中删除一些东西,结果它爆炸了他们说我应该问你”,然后我解释说他们要删除的值正在使用中,所以他们不能这样做。
让我们考虑一个实际的例子。假设您有一个名为 PARENT_TABLE 的表:
CREATE TABLE PARENT_TABLE
(ID_PARENT_TABLE NUMBER
CONSTRAINT PK_PARENT_TABLE
PRIMARY KEY
USING INDEX,
PARENT_ATTR_1 NUMBER,
PARENT_ATTR_2 VARCHAR2(100),
BLAH_BLAH_BLAH VARCHAR2(50));
现在假设有一个名为CHILD_TABLE的子表:
CREATE TABLE CHILD_TABLE
(ID_CHILD_TABLE NUMBER
CONSTRAINT PK_CHILD_TABLE
PRIMARY KEY
USING INDEX,
ID_PARENT_TABLE NUMBER
CONSTRAINT CHILD_TABLE_FK1
REFERENCES PARENT_TABLE(ID_PARENT_TABLE)
ON DELETE CASCADE,
CHILD_ATTR_1 NUMBER,
WHATEVER VARCHAR2(100));
真正起作用的是外键约束 CHILD_TABLE_FK1。当您从 PARENT_TABLE 中删除时,数据库注意到 CHILD_TABLE_FK1 引用了 PARENT_TABLE(ID_PARENT_TABLE),因此数据库说:“嗯……我正在删除 PARENT_TABLE 中 ID_PARENT_TABLE 值为 10 的行 - 我想知道是否有表中是否有任何由这个 CHILD_TABLE_FK1 约束命名的行,这些行的 ID_PARENT_TABLE 值也恰好是 10。嗯,在 Jupiter 上,有!哇——我该怎么办?好吧,约束说,“ON DELETE CASCADE” - 啊哈!所以我会从 CHILD_TABLE 中删除那些 ID_PARENT_TABLE 值与我要删除的行匹配的行,从关系上来说,一切都会与世界保持一致”。现在,如果外键级联指定了ON DELETE SET NULL,那么 CHILD_TABLE 中与要删除的匹配的 ID_PARENT_KEY 值将设置为 NULL。此外,如果您没有指定 ANY ON DELETE 选项,数据库将不知道该做什么 - 您没有告诉它删除匹配的引用,也没有告诉它将匹配的引用设置为 NULL ,所以它会举手(象征性地)并抛出异常(字面意思),因为您不能拥有父级不再存在的子键。
请注意,您可以根据需要继续执行此操作。假设您有另一个表 GRAND_CHILD_TABLE 引用 CHILD_TABLE:
CREATE TABLE GRAND_CHILD_TABLE
(ID_GRAND_CHILD_TABLE NUMBER
CONSTRAINT PK_GRAND_CHILD_TABLE
PRIMARY KEY
USING INDEX,
ID_CHILD_TABLE NUMBER
CONSTRAINT GRAND_CHILD_TABLE_FK1
REFERENCES CHILD_TABLE(ID_CHILD_TABLE)
ON DELETE CASCADE,
what_EVER VARCHAR2(25));
现在,当您从 PARENT_TABLE 中删除时,CHILD_TABLE_FK1 约束将导致 CHILD_TABLE 中的所有匹配行被删除,而 GRAND_CHILD_TABLE_FK1 约束将删除 GRAND_CHILD_TABLE 中 ID_CHILD_TABLE 值与从 CHILD_TABLE 中删除的行匹配的所有行。
祝你好运。
【讨论】: