【问题标题】:Trigger to prevent infinite loop in a sql tree触发以防止 sql 树中的无限循环
【发布时间】:2009-06-13 09:44:21
【问题描述】:

我有(NodeId,NodeName)和结构表(ParentNodeId,ChildNodeId)的节点表。如何编写一个触发器来检查插入更新或删除语句是否会导致无限关系?

【问题讨论】:

    标签: sql tree loops triggers


    【解决方案1】:

    这是我的解决方案,到目前为止它按预期工作。

    CREATE TRIGGER [dbo].[CheckNodeDependence] ON [dbo].[ObjectTrees]
    AFTER INSERT
    AS
    BEGIN
        SET NOCOUNT ON
    
        DECLARE @CTable TABLE(ChildId INT NOT NULL,
                              ParentId INT NOT NULL,
                              [Level] INT NOT NULL,
                              RowId INT NOT NULL)
    
        DECLARE @Level INT
        SET @Level = 1
    
        DECLARE @rows_affected INT
        SET @rows_affected = 1
    
        INSERT INTO @CTable
        SELECT ObjectId, ParentId, 1, ObjectId FROM INSERTED
    
        WHILE @rows_affected > 0
        BEGIN
            SET @Level = @Level + 1
            INSERT INTO @CTable
            SELECT T.ObjectId, T.ParentId, @Level, C.RowId
                FROM ObjectTrees T
                    INNER JOIN @CTable C ON T.ParentId = C.ChildId
                    AND C.Level = @Level - 1
    
                SET @rows_affected = @@rowcount
                IF EXISTS(
                    SELECT * FROM @CTable B
                        INNER JOIN @CTable V ON B.level = 1
                        AND V.Level > 1
                        AND V.RowId = B.RowId
                        AND V.ChildId = B.RowId)
                BEGIN
                        DECLARE @error_message VARCHAR(200)
                        SET @error_message = 'Operation would cause illegal circular reference in tree structure, level = ' + CAST(@Level AS VARCHAR(30))
                        RAISERROR(@error_message,16,1)
                        ROLLBACK TRANSACTION
                        RETURN
                END
            END
        END
    GO
    

    【讨论】:

      【解决方案2】:

      您必须递归地检查循环依赖条件,在这种情况下,父母不会直接或间接地成为自己孩子的孩子。

      在 SQL Server 2005 中,您可以为此编写递归 CTE。一个例子 -

      WITH [RecursiveCTE]([Id], [ParentAccountId]) AS
      (
          SELECT
              [Id],
              [ParentAccountId]
          FROM [Structure]
          WHERE [Id] = @Id
          UNION ALL
          SELECT
              S.[Id],
              S.[ParentAccountId]
          FROM [Structure] S INNER JOIN [RecursiveCTE] RCTE ON S.[ParentAccountId] = RCTE.[Id]
      )
      SELECT * FROM [RecursiveCTE]
      

      【讨论】:

      • 但是一旦找到,使用触发器导致插入/更新失败的最佳方法是什么?
      • 取决于平台。在 Oracle 中,您会抛出异常,希望在文本中提供尽可能多的有用信息。但实际上,这取决于应用程序,不是吗?
      • 是的。这取决于您使用的应用程序。在 SQL Server 中,您还将引发异常以停止插入。或者您可以使用而不是触发器而不做任何事情。
      • 我同意这两个 cmets,我的意思是暗示提问者想知道“如何找到问题”和“如何使用触发器来防止它”:)
      猜你喜欢
      • 2012-05-08
      • 2011-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多