停止递归是一个非常糟糕的主意吗?

时间:2018-03-08 18:34:58

标签: tsql recursion triggers

我一直在SQL Server中构建一些触发器。每个触发器的目的是更新相关记录上的一些数据(因为它们都是)。以下是其中一个触发器的示例:

CREATE TRIGGER dbo.bssProjects_Update
   ON  dbo.Projects
   AFTER UPDATE
AS 
BEGIN
    SET NOCOUNT ON;
-- workflow to reset the pipelinestatus if the totalCharge changes. remember that there can be many changes at once
UPDATE
    Proj
SET
    Proj.[PipelineStage] = 11
FROM
    Inserted INS    -- Project record
    INNER JOIN Deleted DEL ON INS.ID = DEL.ID
    INNER JOIN Projects Proj on Proj.ID = INS.ID
WHERE
    (INS.[PipelineStage] > 11 and INS.[TotalCharge] != DEL.[TotalCharge])


-- workflow to reset the pipelinestatus if the totalCharge changes. remember that there can be many changes at once
UPDATE
    Proj
SET
    Proj.[PipelineStage] = 13
FROM
    Inserted INS    -- Project record
    INNER JOIN Deleted DEL ON INS.ID = DEL.ID
    INNER JOIN InvoiceLines Invce on Invce.bssToProjectID = INS.ID
    INNER JOIN Projects Proj on Invce.bssToProjectID = Proj.ID
WHERE
    (INS.[PipelineStage] > 11 and INS.[TotalCharge] != DEL.[TotalCharge])


-- workflow to reset the pipelinestatus if the totalCharge changes. remember that there can be many changes at once
UPDATE
    Invce_2
SET
    Invce_2.[InvoiceStatus] = 30
FROM
    Inserted INS    -- Project record
    INNER JOIN Deleted DEL ON INS.ID = DEL.ID
    INNER JOIN InvoiceLines Invce on Invce.bssToProjectID = INS.ID
    INNER JOIN Invoices Invce_2 on Invce.bssToInvoiceID = Invce_2.ID
WHERE
    (INS.[PipelineStage] > 11 and INS.[TotalCharge] != DEL.[TotalCharge])


-- workflow to reset the contributor status if the invoicable value changes. 
UPDATE
    Contrib
SET
    Contrib.[Status] = 40
FROM
    Inserted INS    -- Project record
    INNER JOIN Contributors Contrib on Contrib.bssToProjectID = INS.ID
WHERE
    (Contrib.[Status] > 40)
    and (INS.[TranslationCost] != INS.[T1NetInvoiced])


-- on total or cost allocation changes, recalc translator payment.
UPDATE
    Contrib
SET
    Contrib.[Payment] = (INS.[NetCharge] - INS.[DiscountCharge]) * CostAlloc.[TShare],
    Contrib.[ManualPayment] = 0
FROM
    Inserted INS    -- Project record
    INNER JOIN Deleted DEL ON INS.ID = DEL.ID
    INNER JOIN Contributors Contrib on Contrib.bssToProjectID = INS.ID
    INNER JOIN luCostAllocations CostAlloc on INS.CostAllocation = CostAlloc.ID
WHERE
    (Contrib.[ContributorRole] = 2)
    and ((INS.[NetCharge] - INS.[DiscountCharge]) != (DEL.[NetCharge] - DEL.[DiscountCharge]) or INS.[CostAllocation] != DEL.[CostAllocation])


-- on total or cost allocation changes, recalc editor payment.
UPDATE
    Contrib
SET
    Contrib.[Payment] = (INS.[NetCharge] - INS.[DiscountCharge]) * CostAlloc.[EditorShare],
    Contrib.[ManualPayment] = 0
FROM
    Inserted INS    -- Project record
    INNER JOIN Deleted DEL ON INS.ID = DEL.ID
    INNER JOIN Contributors Contrib on Contrib.bssToProjectID = INS.ID
    INNER JOIN luCostAllocations CostAlloc on INS.CostAllocation = CostAlloc.ID
WHERE
    (Contrib.[ContributorRole] = 3)
    and ((INS.[NetCharge] - INS.[DiscountCharge]) != (DEL.[NetCharge] - DEL.[DiscountCharge]) or INS.[CostAllocation] != DEL.[CostAllocation])


-- on total or cost allocation changes, recalc proofreader payment.
UPDATE
    Contrib
SET
    Contrib.[Payment] = (INS.[NetCharge] - INS.[DiscountCharge]) * CostAlloc.[ProofReaderShare],
    Contrib.[ManualPayment] = 0
FROM
    Inserted INS    -- Project record
    INNER JOIN Deleted DEL ON INS.ID = DEL.ID
    INNER JOIN Contributors Contrib on Contrib.bssToProjectID = INS.ID
    INNER JOIN luCostAllocations CostAlloc on INS.CostAllocation = CostAlloc.ID
WHERE
    (Contrib.[ContributorRole] = 4)
    and ((INS.[NetCharge] - INS.[DiscountCharge]) != (DEL.[NetCharge] - DEL.[DiscountCharge]) or INS.[CostAllocation] != DEL.[CostAllocation])


-- on total or cost allocation changes, recalc devexec payment.
UPDATE
    Contrib
SET
    Contrib.[Payment] = (INS.[NetCharge] - INS.[DiscountCharge]) * CostAlloc.[PMShare],
    Contrib.[ManualPayment] = 0
FROM
    Inserted INS    -- Project record
    INNER JOIN Deleted DEL ON INS.ID = DEL.ID
    INNER JOIN Contributors Contrib on Contrib.bssToProjectID = INS.ID
    INNER JOIN luCostAllocations CostAlloc on INS.CostAllocation = CostAlloc.ID
WHERE
    (Contrib.[ContributorRole] = 5)
    and ((INS.[NetCharge] - INS.[DiscountCharge]) != (DEL.[NetCharge] - DEL.[DiscountCharge]) or INS.[CostAllocation] != DEL.[CostAllocation])


-- on total or cost allocation changes, recalc devexec payment.
UPDATE
    Contrib
SET
    Contrib.[Payment] = (INS.[NetCharge] - INS.[DiscountCharge]) * CostAlloc.[DevExecShare],
    Contrib.[ManualPayment] = 0
FROM
    Inserted INS    -- Project record
    INNER JOIN Deleted DEL ON INS.ID = DEL.ID
    INNER JOIN Contributors Contrib on Contrib.bssToProjectID = INS.ID
    INNER JOIN luCostAllocations CostAlloc on INS.CostAllocation = CostAlloc.ID
WHERE
    (Contrib.[ContributorRole] = 6)
    and ((INS.[NetCharge] - INS.[DiscountCharge]) != (DEL.[NetCharge] - DEL.[DiscountCharge]) or INS.[CostAllocation] != DEL.[CostAllocation])

-- clean up any temp tables created in creation process
END

很抱歉有这么多,但我想说明有很多事情要发生。然后我对项目记录做了一个简单的更改 - 一个不影响触发器中使用的任何值的一个,我得到一个错误:

超出最大存储过程,函数,触发器或视图嵌套级别(限制32)。

关于这一点,这意味着发生了太多的递归动作。每次更新都会导致调用另一个触发器 - 即使没有任何更改。实际上,我更改了触发器并用选择替换了所有更新并通过它进行了调试,发现没有正在进行的实际更新。这只是通过触发器来观察和看到。

问题在于我希望触发器按照请求运行,所以我在执行之前首先尝试了测试表达式的策略 - 只有在有某些事情要做时才调用更新。像这样:

declare @rows as int = 0;
select @rows = count(*)
FROM
    Inserted INS    -- Project record
    INNER JOIN Deleted DEL ON INS.ID = DEL.ID
    INNER JOIN Projects THIS on THIS.ID = INS.ID
WHERE
    (INS.[PipelineStage] > 11 and INS.[TotalCharge] != DEL.[TotalCharge])

if (@rows > 0)
BEGIN
    UPDATE
        THIS
    SET
        THIS.[PipelineStage] = 11
    FROM
        Inserted INS    -- Project record
        INNER JOIN Deleted DEL ON INS.ID = DEL.ID
        INNER JOIN Projects THIS on THIS.ID = INS.ID
    WHERE
        (INS.[PipelineStage] > 11 and INS.[TotalCharge] != DEL.[TotalCharge])
END

它肯定会阻止递归。问题是,这是一个真正的NONO吗?我知道它会慢一些,但我认为它只会稍微慢一些,即使有事情要做,因为查询已经存在于实际查询中。

欢迎您的建议 克雷格

1 个答案:

答案 0 :(得分:0)

在触发开始时,检查@@ROWCOUNT是否为零,是否执行RETURN退出触发器。您应该在第一个语句中执行检查(甚至在SET NOCOUNT ON之前),因为任何语句可能会重置@@ROWCOUNT,您可能会得到不同的结果。