Sql 更新后触发困惑

Sql 更新后触发困惑,sql,triggers,sql-server-2008-r2,Sql,Triggers,Sql Server 2008 R2,我正试图让我的头绕过更新后的触发器 目前在我们的数据库中有一个包含游标的触发器。据我所知,触发器中的游标通常表现不佳,因此我正在尝试摆脱游标 当前触发器如下所示: ALTER TRIGGER [dbo].[trg_TaskMovement_Zone] ON [dbo].[Tasks_Movement] AFTER INSERT, UPDATE AS BEGIN SET NOCOUNT ON DECLARE @rowcheck int DECLARE @Mov

我正试图让我的头绕过更新后的触发器

目前在我们的数据库中有一个包含游标的触发器。据我所知,触发器中的游标通常表现不佳,因此我正在尝试摆脱游标

当前触发器如下所示:

    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  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
据我所知,这里的光标完全没有必要(?)

我是否认为以下方法更有效:

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

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

    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  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的
更新。。从
语法,并且在检查站点ID是否大于0时,还删除了冗余的空检查
NULL
不大于或小于0,因此,如果SiteID为NULL,SiteID>0永远无法计算为true,因此这是一个冗余的附加检查

最后,我还建议删除用户定义的函数,尽管我看不到这些函数的本质,但从名称来看,它们看起来非常像简单的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,因此如果更新1000000行,其中一行有更改,它将对所有行执行更新,而不仅仅是对已更改的行,通过加入
deleted
,您可以将更新限制到相关行。

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

    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  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的
更新。。从
语法,并且在检查站点ID是否大于0时,还删除了冗余的空检查
NULL
不大于或小于0,因此,如果SiteID为NULL,SiteID>0永远无法计算为true,因此这是一个冗余的附加检查

最后,我还建议删除用户定义的函数,尽管我看不到这些函数的本质,但从名称来看,它们看起来非常像简单的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,因此如果更新1000000行,其中一行有更改,它将对所有行执行更新,而不仅仅是对已更改的行,通过加入
deleted
,您可以将更新限制在相关行。

请告诉我们这是一个什么样的具体数据库系统-许多内容都是特定于供应商的。你在使用MySQL吗?博士后?SQL Server?神谕IBM DB2?完全是别的吗?请更新您的标签以显示您正在使用的数据库系统(以及它的版本!)-谢谢!对不起,马克。。。它是针对SQL Server的。我现在已经更新了标记。当你浏览一大组行时,光标的移动速度很慢。在您的例子中,您只有插入的行,因此删除光标不是问题?不是这样吗?是的,插入的表包含的行数与更新的行数一样多,可能是数百万行,因此游标的速度会很慢。您试图避免像瘟疫一样的游标是正确的,几乎总是有一种基于集合的方法可以超越它,就像所有东西一样,尽管它们有自己的位置。请告诉我们这是什么具体的数据库系统-许多东西是特定于供应商的。你在使用MySQL吗?博士后?SQL Server?神谕IBM DB2?完全是别的吗?请更新您的标签以显示您正在使用的数据库系统(以及它的版本!)-谢谢!对不起,马克。。。它是针对SQL Server的。我现在已经更新了标记。当你浏览一大组行时,光标的移动速度很慢。在您的例子中,您只有插入的行,因此删除光标不是问题?不是这样吗?是的,插入的表包含的行数与更新的行数一样多,可能是数百万行,因此游标的速度会很慢。你试图避免像瘟疫一样的游标是正确的,几乎总是有一种基于集合的方法可以超越它,就像所有东西一样,尽管它们有自己的位置。啊,好吧,现在这更有意义了。谢谢Gareth的建议。我能用以下内容来包装这些更新语句吗?(UPDATE(FromAddress\u Postcode)或UPDATE(ToAddress\u Postcode))BEGIN。。。停止不必要的更新?太棒了。。。谢谢你帮我解决这个问题。现在它变得更有意义了!:)但是,删除时的内部联接是否会阻止以这种方式更新插入的记录?是的,你可以把它改成一个左连接并添加一个where子句(我已经改变了我以前的编辑来显示这个)啊,好的,这现在更有意义了。谢谢Gareth的建议。我能用以下内容来包装这些更新语句吗?(UPDATE(FromAddress\u Postcode)或UPDATE(ToAddress\u Postcode))BEGIN。。。停止不必要的更新?太棒了。。。谢谢你帮我解决这个问题。现在它变得更有意义了!:)但是,删除时的内部联接是否会阻止以这种方式更新插入的记录?IE它只能在纯更新命令上工作,不能在插入命令上工作?是的,可以将其更改为左连接并添加where子句(我已更改了以前的编辑以显示此内容)