更新后触发逻辑不正确

Posted

技术标签:

【中文标题】更新后触发逻辑不正确【英文标题】:Incorrect After Update trigger logic 【发布时间】:2017-12-23 06:59:07 【问题描述】:

我在触发器中遇到了错误的逻辑。在对基表进行更新后,我将错误的记录数插入到审计表中。下面是重新创建此问题的代码 sn-p。

create table #T1 (id int NOT NULL, CODE varchar(3) NOT NULL, pkID INT NOT NULL)
insert into #T1 (id, CODE, pkID)
values(1, 'vak', 1989),(2,'Vl2',1988), (2,'Vl2',1988)--, (null, 'Gotik')
go

create table #T2 (id int NOT NULL, CODE varchar(3) NOT NULL, pkID INT NOT NULL)
insert into #T2 (id, CODE, pkID)
values(101, 'vas', 1979),(105,'va3',1973), (4,'va5',1888),  (4,'va5',1888)--, (null, 'Popik') 
go 

create table [MyDB].dbo.Test_Dec22nd2017 (id int NOT null CONSTRAINT PK_TEST PRIMARY KEY, Ln varchar(10) null)
insert into [MyDB].dbo.Test_Dec22nd2017 (id, ln)

values (1, 'vasya1'),(2,'vasya3'), (4,'vasya2'),(5,'Super'), (6,'LYAYA'), (105,'TERSDF'), (101,'DFSDS')
go 


DROP TRIGGER DBO.TR_TEST_DEC22nd2017
GO
CREATE TRIGGER DBO.TR_TEST_DEC22nd2017
ON [dbo].[Test_Dec22nd2017] AFTER INSERT, DELETE, UPDATE
    NOT FOR REPLICATION

AS 
BEGIN 

    IF NOT EXISTS ( SELECT TOP 1 1 FROM 
                                inserted ins
                    FULL OUTER JOIN deleted del ON ins.ID = del.ID
                    INNER JOIN 
                    [MyDB].dbo.Test_Dec22nd2017 a ON a.id = ISNULL(ins.ID, del.ID)
                    INNER JOIN #T1 t ON t.id = a.id
                    LEFT JOIN #T2 t2 ON t2.id = a.id
                    --WHERE t.id IS NULL AND t2.id IS NULL 
                )

        BEGIN

            WITH CTE2 AS (
                    SELECT 
                        ISNULL(ins.ID, del.ID) AS ID,
                        CASE 
                            WHEN ins.ID IS NOT NULL AND del.ID IS NOT NULL THEN 'UPDATE' 
                            WHEN ins.ID IS NOT NULL THEN 'INSERT'
                            ELSE 'UPDATE' END
                        AS AuditType  
                    FROM inserted ins 
                    FULL OUTER JOIN deleted del 
                            ON ins.ID = del.ID
                    ) 

            ,AUDIT_CTE2 AS (    
                    SELECT DISTINCT     
                        CTE2.ID, 
                        'OVC' AS Code,
                        CTE2.AuditType
                    FROM CTE2
                    --  WHERE ID IS NOT NULL
                        )

                    INSERT [MyDB].[dbo].[AuditTable] (Code, ID, AuditType)/*, ParentCode, ParentID)*/
                    SELECT
                        Code,
                        ID, 
                        AuditType
                    FROM AUDIT_CTE2
        END 

    IF EXISTS ( SELECT TOP 1 1 FROM 
                                inserted ins -- ISNULL(T.pkID, t2.pkID)
                    FULL OUTER JOIN deleted del ON ins.ID = del.ID
                    INNER JOIN 
                    [MyDB].dbo.Test_Dec22nd2017 a ON a.id = ISNULL(ins.ID, del.ID)
                    LEFT JOIN #T1 t ON t.id = a.id
                    LEFT JOIN #T2 t2 ON t2.id = a.id
                    WHERE t.id IS NOT NULL OR t2.id IS NOT NULL
                ) 

        BEGIN

            WITH CTE AS (
                SELECT 
                    ISNULL(ins.ID, del.ID) AS ID,
                    CASE 
                        WHEN ins.ID IS NOT NULL AND del.ID IS NOT NULL THEN 'UPDATE' 
                        WHEN ins.ID IS NOT NULL THEN 'INSERT'
                        ELSE 'UPDATE' END
                    AS AuditType  
                FROM inserted ins 
                FULL OUTER JOIN deleted del 
                        ON ins.ID = del.ID
                        ) 

            ,AUDIT_CTE AS ( 
                    SELECT DISTINCT     
                        CTE.ID, 
                        'TST' AS Code,
                        CTE.AuditType,
                        ISNULL(T.CODE,T2.CODE) AS ParentCode,
                        ISNULL(T.pkID, T2.pkID) AS ParentID
                    FROM CTE
                    INNER JOIN MyDB.dbo.Test_Dec22nd2017 a ON CTE.ID = a.id
                    LEFT OUTER JOIN #T1 T ON T.id = a.id
                    LEFT OUTER JOIN #T2 T2 ON T2.id = a.id 
                    WHERE T.pkID IS NOT NULL OR T2.pkID IS NOT NULL
                        )

                    INSERT [MyDB].[dbo].[AuditTable] (Code, ID, AuditType, ParentCode, ParentID)
                    SELECT
                        Code,
                        ID, 
                        AuditType,
                        ParentCode,
                        ParentID
                    FROM AUDIT_CTE
        END 
END

这就是在审计表中插入两条错误记录的原因

GO
UPDATE [MyDB].dbo.Test_Dec22nd2017
SET Ln = 'DDD' WHERE ID 
IN (101,105,11)
SELECT TOP 30 * FROM  [MyDB].[dbo].[AuditTable] ORDER BY 1 DESC

正如您从上面的图片中看到的,它将 5 行插入到搜索审计表中。但应该只插入 3 个。一个用于 ID 6 且父代码 = Null 且 ParentID = NULL 以及 ID 101 和 105 的两个记录,其 ParentID 为 1973 和 1979 以及相应的父代码。

这两个语句工作得很好。

insert into [MyDB].dbo.Test_Dec22nd2017 (id, ln)
values (44,'vasya1'), (45,'vasya2')
SELECT TOP 30 * FROM  [MyDB].[dbo].[AuditTable] ORDER BY 1 DESC

GO

UPDATE [MyDB].dbo.Test_Dec22nd2017
SET Ln = 'DDD' WHERE ID
IN (5,10,6)
SELECT TOP 30 * FROM  [MyDB].[dbo].[AuditTable] ORDER BY 1 DESC

【问题讨论】:

通过运行您的代码,我将 4 条记录插入到 AuditTable 中,2 条 OVC 和 2 条 TST 针对 101 和 105,这与您编写触发器的方式是正确的。您的图像显示了一条用 ID 6 标记为正确的记录。那是正确的吗? Tony,对于导致此问题的第一个更新语句,触发器应该只返回 3 条记录,如下所示:ID 为 105 和 101 的 2 条记录,其中 ParentID 不为空,一条 id 为 6 的记录父 ID 为 NULL。这里的问题是它还会插入 id 为 105 和 ParentID 为 NULL 的 101 的记录。 您的问题是为什么要执行第一个插入 OVC 记录的“IF NOT EXISTS...”块? 所写的触发器工作正常。插入 101 和 105 的 OVC 记录是因为它的 Id 在 #T1 中不存在,因为它在第一个 INSERT 的 IF NOT EXISTS 块中检查。你希望它如何改变? 您好 Tony,第一个更新语句插入 ID 为 101 和 105 且 ParentID = NULL 的记录的问题,我不希望第一个更新语句出现这种情况。在该语句中,我只需要 ID 为 101 和 105 且 parentID 不为 NULL 的记录以及 ID 为 6 = NULL 的记录。第二个和第三个 Update 语句工作正常。 【参考方案1】:

您可以将触发器中的第一个 IF NOT EXISTS 更改为:

IF EXISTS (SELECT TOP 1 1 FROM 
                inserted ins
    FULL OUTER JOIN deleted del ON ins.ID = del.ID
    INNER JOIN 
    [tempdb].dbo.Test_Dec22nd2017 a ON a.id = ISNULL(ins.ID, del.ID)
    LEFT JOIN #T1 t ON t.id = a.id
    LEFT JOIN #T2 t2 ON t2.id = a.id
    WHERE t.id IS NULL AND t2.id IS NULL 
)

忽略 #T1 或 #T2 中存在的任何 ID

【讨论】:

托尼,这正是我已经尝试过的,这正是我发布这个问题的原因。这只会重复这个问题。请参阅上面的第二张图片,其中行 id 为 3 和 4。我不想将这两条记录插入到 Audit 表中。 你试过用上面的替换吗?它不应插入专利 id = NULL 的 101 或 105。那是第二张图片中的那两个错误行。对我来说没有 是的,我用上面的代码替换了您的代码,我将 5 条记录插入到审核表中,与第二张图像上的完全相同。

以上是关于更新后触发逻辑不正确的主要内容,如果未能解决你的问题,请参考以下文章

触发检查逻辑日期更新

MySQL触发器的使用规则

jQuery 更改为隐藏字段后,在 Gravity Forms 中触发表单更新

Sharepoint 2013 本地 NewForm 列表 javascript 逻辑未触发

tSQL 触发逻辑,最好有 IF 子句,还是 IN 子句?

Spring云配置刷新后如何执行自定义逻辑?