【问题标题】:Oracle - delete all child records for a parentOracle - 删除父项的所有子记录
【发布时间】:2016-09-08 19:56:45
【问题描述】:

我很难找到一种简单的方法来删除父 ID 的所有子记录。子表也可能有自己的子表,因此我们需要删除层次结构中的所有记录。什么方法最简单?我可以手动转到每个子表并找到它的子表,然后创建一个脚本,但是表太多了,想知道一种更简单的方法。任何帮助表示赞赏!

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    这几乎就是主键和外键以及像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
    

    【讨论】:

    • 是的,我们已经设置了外键。我只需要知道层次结构中的表名和引用的列名。然后我可以编写脚本先删除最底部的子表,然后分别删除其余子表。
    • 就像@mathguy 提到的那样使用级联删除。无需编写自下而上的删除
    • 我想我明白 OP 的目的是什么......如果 FK 没有在级联删除时设置怎么办?他/她需要一种列出所有后代的方法,以验证 FK 是否设置为 ON CASCADE DELETE。为了我自己的学习,我正在研究如何做到这一点 - 要么使用工具(我相信一定有一些),要么使用针对 ALL_CONSTRAINTS 和 ALL_CONS_COLUMNS 的分层查询。
    • 在删除所有后代时也需要非常小心。另一个选项是ON DELETE SET NULL,在某些情况下这是正确的设置。例如,如果一个员工表有 emp_id (PK) 和 mgr_id(FK 指向同一个表中的 emp_id),并且从表中删除了经理行,那么在几乎所有情况下,该经理的员工的 mgr_id 字段都应设置为null 直到进一步的指示(直到分配了新的经理);所有经理的直接下属的行不应该与经理的行一起从表中删除。
    • @mathguy:在某些情况下,不使用ON DELETE 选项中的either 是正确的做法。在我们的数据库中,我们有验证表,以确保在各个字段中仅使用“有效”值。我有许多引用这些表的外键,这些表故意没有指定ON DELETE 选项,而且我经常接到某人的电话,说:“嘿,我试图从 VAL_UMPTY_SQUAT 表中删除一些东西,结果它爆炸了他们说我应该问你”,然后我解释说他们要删除的值正在使用中,所以他们不能这样做。
    【解决方案2】:

    让我们考虑一个实际的例子。假设您有一个名为 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 中删除的行匹配的所有行。

    祝你好运。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-27
      • 2012-02-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多