【问题标题】:Self-referencing constraint in MS SQLMS SQL 中的自引用约束
【发布时间】:2009-02-09 15:10:54
【问题描述】:

MS SQL 是否使用 ON DELETE CASCADE 选项限制自引用约束? 我有一个具有父子关系的表,PARENT_ID 列是 ID 的外键。使用 ON DELETE CASCADE 选项创建它会导致错误

"引入 FOREIGN KEY 约束 可能导致循环或多个级联 路径。指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。”

我不敢相信我必须以递归模式删除这个层次结构。除了触发器还有其他问题吗?

【问题讨论】:

    标签: sql sql-server sql-server-2005 tsql


    【解决方案1】:

    在这种情况下,您无法在具有自引用约束的表上设置 ON DELETE CASCADE。存在潜在的循环逻辑问题,因此不允许这样做。

    有一篇很好的文章 here - 虽然它适用于 SQL 的第 8 版而不是第 9 版 - 虽然适用相同的规则。

    【讨论】:

    • PostgreSQL 支持它。现在如果另一个不被删除的条目引用了一个要删除的子条目,它将发出错误,并且删除操作将被回滚。否则,它将完美运行。真正的问题是,您的父子关系是否是非循环有向图。因为大多数自引用都实现了层次结构,并且层次结构应该是有向非循环图,所以在大多数情况下,PostgreSQL 的操作过程肯定会更加理智。在所有其他情况下,您仍然可以手动实现该功能,因此不会丢失任何内容。
    【解决方案2】:

    我刚刚回答了another question,其中 问题被绑定为重复。我认为我的答案也值得在这里:

    这是不可能的。你可以通过INSTEAD OF TRIGGER解决这个问题

    create table locations 
    (
        id int identity(1, 1),
        name varchar(255) not null,
        parent_id int,
    
        constraint pk__locations
            primary key clustered (id)
    
    )
    GO
    
    INSERT INTO locations(name,parent_id)  VALUES
     ('world',null)
    ,('Europe',1)
    ,('Asia',1)
    ,('France',2)
    ,('Paris',4)
    ,('Lyon',4);
    GO
    

    --此触发器将使用递归 CTE 来获取您要删除的所有 ID 之后的所有 ID。这些 ID 将被删除。

    CREATE TRIGGER dbo.DeleteCascadeLocations ON locations
    INSTEAD OF DELETE 
    AS
    BEGIN
        WITH recCTE AS
        (
            SELECT id,parent_id
            FROM deleted
    
            UNION ALL
    
            SELECT nxt.id,nxt.parent_id
            FROM recCTE AS prv
            INNER JOIN locations AS nxt ON nxt.parent_id=prv.id
        )
        DELETE FROM locations WHERE id IN(SELECT id FROM recCTE);
    END
    GO
    

    --在这里测试一下,用不同的ID试试。你也可以试试WHERE id IN(4,3)...

    SELECT * FROM locations;
    
    DELETE FROM locations WHERE id=4;
    
    SELECT * FROM locations
    GO
    

    --清理(注意真实数据!)

    if exists(select 1 from INFORMATION_SCHEMA.TABLES where TABLE_NAME='locations')
    ---DROP TABLE locations; 
    

    【讨论】:

    • 我有一个表,它引用自身的深度可达三层,对此进行了广泛的测试,它运行良好。非常感谢!
    【解决方案3】:
    CREATE TRIGGER MyTable_OnDelete ON MyTable
    INSTEAD OF DELETE
    AS 
    BEGIN
    
      SET NOCOUNT ON;
    
      DELETE FROM mt
      FROM   deleted AS D
      JOIN   MyTable AS mt
      ON     d.Id = mt.ParentId
    
      DELETE FROM mt
      FROM   deleted AS D
      JOIN   MyTable AS mt
      ON     d.Id = mt.Id
    
    END
    

    【讨论】:

    • 抱歉,如果MyTable 与另一个表有关系(具有ON DELETE CASCADE...),您无法定义INSTEAD OF DELETE 触发器...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-24
    • 1970-01-01
    • 2010-10-18
    • 1970-01-01
    相关资源
    最近更新 更多