更新后触发拼图

时间:2013-11-28 11:37:54

标签: sql triggers sql-server-2008-r2

我试图让我的头围绕一个AFTER UPDATE触发器。

目前在我们的数据库中有一个包含游标的Trigger。根据我的理解,触发器中的游标通常表现不佳,所以我试图摆脱光标。

目前触发器如下所示:

    ALTER TRIGGER [dbo].[trg_TaskMovement_Zone] ON [dbo].[Tasks_Movement] 
AFTER INSERT, UPDATE
AS 
BEGIN
    SET NOCOUNT ON

    DECLARE @rowcheck int
    DECLARE @MovementID INT
    DECLARE @SiteFromID INT
    DECLARE @SiteToID INT
    DECLARE @SiteResponsibleID INT
    DECLARE @FromAddress_Postcode Varchar(20)
    DECLARE @ToAddress_Postcode Varchar(20)

    DECLARE zcursor CURSOR FOR SELECT ID, SiteFromID, SiteToID, SiteResponsibleID
    , FromAddress_Postcode, ToAddress_Postcode FROM inserted 

    OPEN zcursor    

    SELECT @rowcheck=1
    WHILE @rowcheck=1       
    BEGIN  
        FETCH NEXT FROM zcursor INTO @MovementID, @SiteFromID, @SiteToID, @SiteResponsibleID, @FromAddress_Postcode, @ToAddress_Postcode
        IF (@@FETCH_STATUS = 0)

        BEGIN
            UPDATE  Tasks_Movement
            SET     ZoneFromID = dbo.fn_GetZoneFromPostcode(@FromAddress_Postcode),
                    ZoneToID = dbo.fn_GetZoneFromPostcode(@ToAddress_Postcode)
            WHERE   Tasks_Movement.ID = @MovementID

            UPDATE  Tasks_Movement
            SET     SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](@SiteFromID) 
            WHERE   Tasks_Movement.ID = @MovementID
                AND (@SiteResponsibleID Is NULL OR @SiteResponsibleID=0)
                AND (@SiteFromID Is NOT NULL AND @SiteFromID>0)

            UPDATE  Tasks_Movement
            SET     SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](@SiteToID)
            WHERE   Tasks_Movement.ID = @MovementID
                AND (@SiteResponsibleID Is NULL OR @SiteResponsibleID=0)
                AND (@SiteToID Is NOT NULL AND @SiteToID>0)


        END
        ELSE
            SELECT @rowcheck=0
    END

    CLOSE zcursor   
    DEALLOCATE zcursor
END

从我可以告诉光标这是完全没必要的(?)

我认为以下内容会更好:

ALTER TRIGGER [dbo].[trg_TaskMovement_Zone] ON [dbo].[Tasks_Movement] 
   AFTER INSERT, UPDATE
AS 
BEGIN
    SET NOCOUNT ON

            UPDATE  Tasks_Movement
            SET     ZoneFromID = dbo.fn_GetZoneFromPostcode(inserted.FromAddress_Postcode),
                    ZoneToID = dbo.fn_GetZoneFromPostcode(inserted.ToAddress_Postcode)
            FROM inserted
            WHERE   Tasks_Movement.ID IN (SELECT id FROM inserted)

            UPDATE  Tasks_Movement
            SET     SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](inserted.SiteFromID) 
            FROM inserted
            WHERE   Tasks_Movement.ID IN (SELECT id FROM inserted
                                            WHERE (inserted.SiteResponsibleID Is NULL OR inserted.SiteResponsibleID=0)
                                            AND (inserted.SiteFromID Is NOT NULL AND inserted.SiteFromID>0))

            UPDATE  Tasks_Movement
            SET     SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](@SiteToID)
            FROM    inserted
            WHERE   Tasks_Movement.ID IN (SELECT id FROM inserted
                                            WHERE (inserted.SiteResponsibleID Is NULL OR inserted.SiteResponsibleID=0)
                                            AND (inserted.SiteToID Is NOT NULL AND inserted.SiteToID>0))

END

1 个答案:

答案 0 :(得分:1)

我认为你的触发器应该是这样的:

ALTER TRIGGER [dbo].[trg_TaskMovement_Zone] ON [dbo].[Tasks_Movement] 
   AFTER INSERT, UPDATE
AS 
BEGIN
    SET NOCOUNT ON

    UPDATE  tm
    SET     ZoneFromID = dbo.fn_GetZoneFromPostcode(i.FromAddress_Postcode),
            ZoneToID = dbo.fn_GetZoneFromPostcode(i.ToAddress_Postcode)
    FROM    Tasks_Movement tm
            INNER JOIN inserted i
                ON i.ID = tm.ID;

    UPDATE  tm
    SET     SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](i.SiteFromID)
    FROM    Tasks_Movement tm
            INNER JOIN inserted i
                ON i.ID = tm.ID
    WHERE   (i.SiteResponsibleID IS NULL OR i.SiteResponsibleID = 0)
    AND     i.SiteFromID > 0

    UPDATE  tm
    SET     SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](i.SiteToID)
    FROM    Tasks_Movement tm
            INNER JOIN inserted i
                ON i.ID = tm.ID
    WHERE   (i.SiteResponsibleID IS NULL OR i.SiteResponsibleID = 0)
    AND     i.SiteToID > 0
END

我已将其更改为使用SQl Server的UPDATE .. FROM语法,并在检查网站ID>时删除了冗余空检查。 0. NULL不大于或小于0,因此如果SiteID为null,则SiteID> 0永远无法评估为真,因此这是一项冗余的额外检查。

最后,我还建议删除用户定义的函数,虽然我看不到它们的基础,根据名称看起来非常像它们是简单的loukup函数,可以通过连接更有效地实现


修改

我不会使用UPDATE(column)函数,而是在更新中添加一个额外的连接来过滤更新的行,例如:

UPDATE  tm
SET     SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](i.SiteToID)
FROM    Tasks_Movement tm
        INNER JOIN inserted i
            ON i.ID = tm.ID
        LEFT JOIN deleted d
            ON d.ID = i.ID
WHERE   (i.SiteResponsibleID IS NULL OR i.SiteResponsibleID = 0)
AND     i.SiteToID > 0
AND     AND ISNULL(i.SiteToID, 0) != ISNULL(d.SiteToID);

我这样做是因为如果任何行有更新的值,UPDATE(siteToID)将返回true,所以如果你更新1,000,000行而其中一行有更改,它将执行更新通过加入deleted,您可以将更新限制为相关的行,而不仅仅是已更改的那些。