插入后触发多次插入后无法正常工作

Posted

技术标签:

【中文标题】插入后触发多次插入后无法正常工作【英文标题】:Trigger after insert not working correctly after multiple inserts 【发布时间】:2019-04-01 14:59:57 【问题描述】:

我有一个在插入后触发的触发器。它触发一个存储过程,更新表CompanyStatus中的列IsLast

Id | CompanyId | Status | DateCreated | IsLast

此存储过程应在IsLast 列中设置1 以获取最大Id 的记录。

CREATE PROCEDURE [dbo].[spCompanyStatusAfterInsert](@schemaName nvarchar(100),  @dataTable CompanyStatus READONLY)
AS
BEGIN
    DECLARE @sql nvarchar(max)

    SELECT @sql = N'DECLARE
        @Id bigint,
        @companyId uniqueidentifier;

        DECLARE CompanyStatus_Cursor CURSOR FAST_FORWARD FOR
        SELECT Id, CompanyId
        FROM @dataTable

        OPEN CompanyStatus_Cursor 

        FETCH NEXT FROM CompanyStatus_Cursor 
        INTO @Id, @companyId

        WHILE @@FETCH_STATUS = 0
        BEGIN
            UPDATE ' + QUOTENAME(@schemaName ) + N'.[CompanyStatus]
            SET IsLast = 0
            WHERE Id IN (SELECT Id
                         FROM ' + QUOTENAME(@schemaName) + N'.[CompanyStatus]
                         WHERE Id <> @Id
                               AND CompanyId = @companyId
                               AND IsLast = 1) 

            UPDATE ' + QUOTENAME(@schemaName) + N'.[CompanyStatus]
            SET IsLast = 1
            WHERE Id = @Id

            FETCH NEXT FROM crs
            INTO @Id, @companyId
        END

        CLOSE CompanyStatus_Cursor 
        DEALLOCATE CompanyStatus_Cursor'

    EXEC sp_executesql @sql, N'@schemaName nvarchar(100), @dataTable CompanyStatus READONLY', @schemaName, @dataTable
END

(首先将所有现有的IsLast 更新为0,然后将1 设置为最新的Id - 这是预期的行为)。

问题 - 当我在很短的时间内进行 2 次插入时,我得到 2 条记录为 IsLast = 1

Id | CompanyId                            | Status | DateCreated             | IsLast
19 | 8afbd9cb-02f9-45d7-a605-a54052866bd4 | 1      | 2019-04-01 12:08:59.540 | 1
18 | 8afbd9cb-02f9-45d7-a605-a54052866bd4 | 1      | 2019-04-01 12:10:57.790 | 1
17 | fc7d1f2b-a72a-4d7f-99fa-602c72fb0410 | 2      | 2019-03-30 12:14:57.294 | 1
16 | 8afbd9cb-02f9-45d7-a605-a54052866bd4 | 1      | 2019-03-29 12:10:57.790 | 0

在 ID 为 18 的示例行中不应使用 IsLast = 1,因为存在 ID 较大的行。


这个更新查询的执行时间有问题吗? (第一次更新在第二次开始之前没有完成)。


触发码:

CREATE TRIGGER [schema].[schema_TRG_CompanyStatus]
ON [schema].[CompanyStatus]
AFTER INSERT
AS
BEGIN
    SET NOCOUNT ON

    DECLARE @dataTable CompanyStatus

    INSERT INTO @dataTable SELECT Id, CompanyId  FROM inserted

    EXEC [dbo].[spCompanyStatusAfterInsert] N'schema', @dataTable
END

GO

【问题讨论】:

为什么你的SP在没有动态语句的情况下使用sp_executesql?以及为什么 CURSOR 看起来可以通过触发器本身中的基于集合的操作轻松实现。 那实际上不是您的实际 SP 吗?如果是这样,要调试它,我们确实需要查看您的真实代码。如果那是您的实际 SP,那么您的评论无法回答我的问题。 您的逻辑在这里确实存在很大缺陷。你有一个光标,你似乎期望订单。但你没有订单。然后,您通过游标在每次迭代中更新整个表,并有效地撤消您在上次迭代中所做的更改。为什么???这尖叫着糟糕的设计和xy problem。 我建议对您想要的更新进行一次更新比这里的光标更有意义。 也许您最好向我们展示您插入的数据,然后向我们展示您所追求的结果以及原因。 【参考方案1】:

测试一下

CREATE PROCEDURE [dbo].[spCompanyStatusAfterInsert](@schemaName nvarchar(100),  @dataTable CompanyStatus READONLY)
AS
BEGIN
    DECLARE @sql nvarchar(max)

    SELECT @sql = N'

            UPDATE tab
            SET IsLast = CASE src.id WHEN tab.id THEN 1 ELSE 0 END

            FROM ' + QUOTENAME(@schemaName ) + N'.[CompanyStatus] tab
            INNER JOIN @dataTable src
            ON tab.CompanyId = src.companyId
            WHERE tab.id = src.id or tab.IsLast=1
    '

    EXEC sp_executesql @sql, N'@schemaName nvarchar(100), @dataTable CompanyStatus READONLY', @schemaName, @dataTable
END

【讨论】:

以上是关于插入后触发多次插入后无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

php-从php插入后未触发MySQL触发器

插入触发器后MySQL不起作用

半计算列只能在插入触发器后使用?

完成表中的数据插入后如何触发触发器

从插入触发器更新时更新触发器后不触发

插入后PostgreSQL触发函数更新