【问题标题】:AFTER INSERT trigger calling stored procedure which calls API, causes problem with @@IDENTITYAFTER INSERT 触发器调用调用 API 的存储过程,导致 @@IDENTITY 出现问题
【发布时间】:2021-08-09 07:31:33
【问题描述】:

我正在为一个客户端替换一个经典的 ASP 网站和 VB6 后端进程。这是一个非常高流量的站点,各种进程将记录插入到队列表中。几个进程是不同的 Web 服务,一个进程是经典 ASP 网站上的表单。对于新进程,我创建了一个调用存储过程的AFTER INSERT 触发器,该存储过程对另一个服务器进行 API 调用。该 API 调用将启动代码来处理新记录。这是INSERT(免责声明:修改代码以掩盖客户身份......让我看起来尽可能聪明):

    -- =============================================
    -- Author:      Gary Jorgenson, RN
    -- Create date: 05/17/2021
    -- Description: notify system of New record
    -- =============================================

    CREATE TRIGGER [dbo].[tr_notify_new_record] 
    ON [dbo].[TableName] 
    AFTER INSERT
    AS 
    BEGIN
        DECLARE @id AS INT;
        SELECT @id = I.id FROM INSERTED I

        EXEC [dbo].[core_api_notify_new_record]
                    @record_id = @id
    END

存储过程如下:

    -- =============================================
    -- Author:      Gary Jorgenson, RN
    -- Create date: 5/16/2021
    -- Description: Notify system of new record
    -- =============================================

    CREATE PROCEDURE [dbo].[usp_api_newrecord_notify]
        @record_id AS INT 
    AS
    BEGIN
        DECLARE @URL NVARCHAR(128) = 'https://ourwebsite.com/api/newrecord';
        DECLARE @recID AS VARCHAR(50) = CONVERT(VARCHAR(50), @record_id);
        SET @URL = CONCAT(@url, '/',  @recID);

        DECLARE @Object AS INT;
        DECLARE @ResponseText AS VARCHAR(8000);

        EXEC sp_OACreate 'MSXML2.XMLHTTP', @Object OUT;
        EXEC sp_OAMethod @Object, 'open', NULL, 'post', @URL,'false'

        EXEC [sp_OAMethod] @Object , 'setRequestHeader' , NULL , 'Content-Type' , 'application/json'
        EXEC [sp_OAMethod] @Object , 'responseText' , @ResponseText OUTPUT

        SELECT @ResponseText AS [Details]

        EXEC [sp_OADestroy] @Object 
    END

为了进行初始测试,我让 API 控制器只是简单地插入到记录新创建记录的记录 ID 的日志表中。我使用 POSTMAN 在本地测试了 API,并且成功了。然后我锁定了 sp_OA... 方法以仅提供必要的权限,并通过使用与生产中使用的相同凭据在 SSMS 中执行存储过程来测试存储过程,这很成功。最后,我启用了AFTER INSERT 触发器,它非常自信地不会出错。

我错了。

打开触发器几分钟后,一些客户打电话报告说网站在尝试提交表单时崩溃了。与最初的网站开发人员合作,我们确定他的代码正在执行多语句 SQL 插入,其中最后一条语句调用 @@IDENTITY 以获取新记录 ID。不知何故,存储过程和/或 API 调用影响了@@IDENTITY,它返回了一个空值或零值。

这是没有意义的,因为在这个过程中唯一的其他 INSERT 是在 API 控制器后面,它在不同的机器上,在不同的 SQL Server 实例中插入日志记录。

原来的网站开发者将@@IDENTITY改为使用SCOPE_IDENTITY()。我们将进行测试,看看这是否能缓解问题。整件事让我感到紧张,因为我从没想过这个过程会对@@IDENTITY 产生任何影响,因为我没有在本地机器上的任何表中插入任何记录。我想更好地了解这个过程中发生了什么。

最后我的问题是,我的流程如何影响@@IDENTITY,因为我没有插入任何记录?

非常感谢任何建议!

【问题讨论】:

  • 好点,谢谢。我将修改触发器以不将任何 id 传递给 proc,并让 proc 扫描新记录。抱歉的问题不是更清楚。我想知道当我没有在我的流程中创建任何新记录时,我的流程如何影响@@IDENTITY。
  • 另外,我的代码中没有使用@@IDENTITY。这是我的新流程破坏的其他人编写的遗留代码。我的第一反应是询问原始开发人员是否可以将 @@IDENTITY 更改为 IDENT_CURRENT(tableName)
  • 即使插入在另一台机器上,在完全不同的 SQL Server 实例中?
  • 啊!这就说得通了。我没有考虑将未知插入到系统表中,这促使我在这里发帖。我的代码引起了我认为不应该引起的事情。我会将您关于 @@IDENTITY 的建议转达给其他开发人员,因为我无法更改他的代码。
  • 您的触发器中有一个根本性错误:它假定inserted 中只有一行,这根本不正确,请参阅brentozar.com/archive/2019/05/…。另外,你真的不应该使用sp_OA... 过程,因为它们很古老并且有很多问题,绝对不要在触发器中使用它们,因为事务将锁定直到收到响应

标签: sql-server tsql stored-procedures triggers webapi


【解决方案1】:

根据docs

@@IDENTITY 和 SCOPE_IDENTITY 返回当前会话中任何表中生成的最后一个标识值。但是,SCOPE_IDENTITY 只返回当前范围内的值; @@IDENTITY 不限于特定范围。

因此,使用@@IDENTITY 时的可能情况是触发器也在执行插入(在您的情况下可能是系统表),因此这个新 id 在@@IDENTITY 中返回给用户 - 这当然是不是他们想要的id。

我真的想不出你想要使用@@IDENTITY 的情况,现在你通常会使用OUTPUT 子句来确保你准确地返回你正在寻找的ID。如果由于某种原因这不是一个选项,那么SCOPE_IDENTITY()@@IDENTITY 的更好替代方案。

IDENT_CURRENT(TableName) 一样糟糕,因为返回的值是跨 ALL 会话和 ALL 范围...您以扩大范围。

注意:顺便说一句,您的触发器已损坏,因为它假设 Inserted 将仅包含一条记录,而实际上它可以包含 0-N。

【讨论】:

  • 感谢您的宝贵建议。我通常将 IDENT_CURRENT(tableName) 与 INSERT 语句一起使用,并且不知道 OUTPUT。我会对此进行研究并从现在开始使用它。
  • @GaryJorgenson this article 可能会感兴趣。
【解决方案2】:

原来是我自己的错。在存储过程中,我在返回 API 响应的过程末尾有一个SELECT

   SELECT @ResponseText AS [Message]

之前的开发者有他在 INSERT 中执行的代码,然后向 @@IDENTITY 查询新的记录 ID。他从 API 调用中获取消息,而不是获取整数记录 ID。他将代码更改为使用SCOPE_IDENTITY() 而不是@@IDENTITY,我们测试发现它仍然无法正常工作。再深入一点,我们发现了问题。

【讨论】:

  • 输出参数绝对是你的朋友,将少量的值返回给调用者。
  • 是的,同意。但在这种情况下,似乎一个问题导致了另一个问题。如果 API 出现问题并返回错误消息,我只真正关心返回值。我注释掉了 select 语句,计划用在 API 失败时使用 SQL Mail 向技术支持发送消息的逻辑替换它。然后我发现 SQL Express(这是我的客户端当前使用的)不支持 SQL Mail。在升级到完整的 SQL Server 之前,我必须重新考虑这种情况。
  • 在一个表中记录一个偶数,写一个小的windows服务器来读取表并发送电子邮件。几个小时的工作:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多