每次更新都不触发触发器

时间:2018-04-30 10:59:27

标签: sql sql-server

我创建了一个触发器来触发表AFTER UPDATE的{​​{1}}。这应该是在销售订单的状态更改为“已选择”时进行监控,以便在此阶段生成要创建高级装运通知的请求。

SOP_Head

当我尝试使用以下更新触发此触发器时,它仅触发第一个和最后一个订单号(ORD117196 AND ORD117199)。

CREATE TRIGGER OrderPickedCreateASN ON SOP_Head AFTER UPDATE
AS
BEGIN
    -- Check the new status of the order
    DECLARE @sopstatus  SMALLINT
    SELECT @sopstatus=SOPSTATUS FROM inserted

    -- Get the SOPNUMBE
    DECLARE @sopnumbe   CHAR(21)
    SELECT @sopnumbe=SOPNUMBE FROM inserted

    -- A SOPSTATUS of 4 means the order has been set to picked
    -- At this stage, create an ASN export request
    IF @sopstatus=4
    BEGIN
        -- Create the ASN export record
        EXEC AddOrderASNExportRequest @in_sopnumbe=@sopnumbe
    END
    -- If any other status, remove any existing requests
    ELSE
    BEGIN
        EXEC RemoveOrderASNExportRequest @in_sopnumbe=@sopnumbe
    END
END
GO

如果我将上述语句调整为仅包含未生成ASN导出请求的两个剩余订单(ORD117197和ORD117198),则会创建它们而不会出现问题。

为什么UPDATE SOP_Head SET SOPSTATUS=4 WHERE SOPNUMBE IN ('ORD117196','ORD117197','ORD117198','ORD117199') 的所有四次更新都没有触发触发器?

2 个答案:

答案 0 :(得分:2)

您的代码假定inserted只有一行。 inserted是一个可以包含多行的视图。

这是完全错误的。您将需要遍历inserted以执行每次更新的一个exec。

可能最安全的方法是使用游标 - 因为您在触发器中使用存储过程。这使得代码比原本需要的要复杂得多。

如果你有一个唯一的ID,我可能会这样做:

declare @i int;
declare @n int;

select @n = count(*), @i = 1 from inserted;

while @i <= n
begin
    with i as (
          select i.*, row_number() over (order by id) as seqnum
          from inserted
         )
    select @sopstatus = SOPSTATUS, @sopnumbe = SOPNUMBE
    from inserted
    where seqnum = @i;

    set @i := @i + 1;

    IF @sopstatus=4
    BEGIN
        -- Create the ASN export record
        EXEC AddOrderASNExportRequest @in_sopnumbe=@sopnumbe
    END
    -- If any other status, remove any existing requests
    ELSE
    BEGIN
        EXEC RemoveOrderASNExportRequest @in_sopnumbe=@sopnumbe
    END

end;  -- while

如果可以同时插入行,这实际上比游标贵一点,但我觉得编码更容易。

答案 1 :(得分:1)

我可以想到两个解决问题的一般方法。

其中一个实际上是您实现的 - 使用存储过程。但是,我建议从SOP_Head中为大量记录触发一次存储过程的执行。 SQL的强大功能在于处理一组记录,而不是逐个循环。当然,这需要对您的SP进行一些重新设计。可能的实现可能如下:

--this UDTT could be defined in a more generic way for a good reuse
CREATE TYPE dbo.SopNrArray AS TABLE 
(
    SopNumber CHAR(21) PRIMARY KEY
)
GO

CREATE TRIGGER OrderPickedCreateASN ON SOP_Head AFTER UPDATE
AS
BEGIN
    DECLARE @sopstatusADD dbo.SopNrArray;
    INSERT @sopstatusADD
    SELECT DISTINCT SOPNUMBE FROM inserted WHERE SOPSTATUS=4;
    --consider removing DISTINCT if all SOPNUMBE would be unique

    DECLARE @sopstatusREM dbo.SopNrArray;
    INSERT @sopstatusREM
    SELECT DISTINCT SOPNUMBE FROM inserted WHERE SOPSTATUS!=4;
    --consider removing DISTINCT if all SOPNUMBE would be unique


    -- This check could eventually be removed if considered to be more optimal
    IF EXISTS (SELECT NULL FROM @sopstatusADD)
    BEGIN
        -- Create the ASN export record
        EXEC AddOrderASNExportRequest @sopstatusADD
    END

    -- This check could eventually be removed if considered to be more optimal
    IF EXISTS (SELECT NULL FROM @sopstatusREM)
    BEGIN
        EXEC RemoveOrderASNExportRequest @sopstatusREM
    END
END
GO

另一种方法是使用另一个专用表实现某种FIFO队列,然后使用经常足够的预定SQL代理作业来处理记录。这有以下主要优点:

  • 对于未来的实施,它有很多可扩展性

  • 不会影响主流的性能。