【问题标题】:Is it possible to get output result set from SQL Server stored procedure before the trigger executes?是否可以在触发器执行之前从 SQL Server 存储过程中获取输出结果集?
【发布时间】:2018-06-20 10:34:24
【问题描述】:

我有一个主表Din_Test_Procedure

CREATE TABLE Din_Test_Procedure(Emp_ID INT);

并且该表的审计表是:

CREATE TABLE Din_Test_Procedure_Audit(Emp_ID INT,LoopingValue BIGINT);

我为表写了一个触发器,当INSERT发生时,100万条记录插入到审计表中:

CREATE TRIGGER TrgAfterInsert_SPTest 
ON [dbo].[Din_Test_Procedure] 
FOR INSERT
AS
    DECLARE @CurID INT, @MIN INT = 1, @MAX INT = 1000000

    SELECT @CurID = i.Emp_ID 
    FROM inserted i;    

    WHILE @MIN <= @MAX
    BEGIN
        INSERT INTO Din_Test_Procedure_Audit 
        VALUES(@CurID, @MIN)

        SET @MIN = @MIN + 1
    END
GO

我还创建了一个带有OUTPUT 参数的存储过程来将记录插入到主表中。

CREATE PROCEDURE Din_Insert_SPTest 
    @ID INT, @ErrCode INT OUTPUT
AS
BEGIN
    INSERT INTO Din_Test_Procedure 
        SELECT @ID

    SELECT @ErrCode = 0
END

当我执行该过程时,它会在触发器执行完成后显示OUTPUT。给出输出需要一些时间。

DECLARE @ErrCode INT
EXEC Din_Insert_SPTest 1,@ErrCode OUTPUT
SELECT @ErrCode

是否可以在主表插入完成后获取输出,即在触发过程完成之前获取输出。

【问题讨论】:

  • 您的触发器有 MAJOR 缺陷,因为您似乎认为它会每行调用一次 - 那是不是 b> 情况。触发器将每个语句触发一次,因此如果您的INSERT 语句导致此触发器触发插入 25 行,您将触发触发器一次,但随后Inserted 伪表将包含 25 行。您的代码将在这 25 行中选择哪一行? SELECT @CurID = i.Emp_ID FROM inserted i; - 它是不确定的,您将获得任意行,并且您将忽略所有其他行。您需要重写触发器以考虑到这一点!

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


【解决方案1】:

没有。
只有在触发器完成运行后,SQL Server 才会将控制权返回给存储过程。
这意味着SELECT @ErrCode = 0 只会在触发器执行之后发生,当然,您只能从存储过程它完成它运行之后返回结果。

原因是触发它的触发器和语句是在隐式事务中执行的。 有关详细信息,请参阅 Microsoft 的 DML Triggers 文档

【讨论】:

  • @ArijitMukherjee 据我所知,没有。 DML 触发器不能与引发它们的语句分开运行。
  • @ArijitMukherjee 可以让触发器更新不同表中的标志并创建计划作业以根据该标志执行任何耗时的工作,但这会使触发器与操作断开连接并且可能是一个坏主意。此外,您的计划任务必须经常对其他表进行采样。
【解决方案2】:

您正在循环中生成一系列数字。然后将每个值插入到表中。换句话说,您的代码是通过痛苦的行来处理数据行。您可以尝试优化您的代码。例如,在您的触发器中使用以下技术插入序列:

CREATE TRIGGER TrgAfterInsert_SPTest ON [dbo].[SP_Test_Procedure] 
FOR INSERT
AS
    DECLARE @CurID INT, @MIN INT=1, @MAX INT=1000000

    SELECT @CurID=i.Emp_ID FROM inserted i; 

    INSERT INTO SP_Test_Procedure_Audit VALUES(@CurID,@MIN)
    SELECT TOP (@Max-@Min+1) @CurID,  @Min-1+row_number() over(order by t1.number) as N
    FROM master..spt_values t1 
    CROSS JOIN master..spt_values t2


GO

您还可以检查这个answer 和这个article 的其他替代方案来生成序列。

如果这还不够,您可以移除触发器并延迟安排插入,前提是这符合您的业务需求。

【讨论】:

  • 感谢朋友的回复。我做循环以获得一些延迟,因为它只是样本。但是实际查询会执行一些其他过程,并且会有所延迟。我不想提高我的查询性能,我想要触发器执行之前的结果集。
  • @DineshDB 我明白了 :-) 也许你需要实现一些调度。您可以创建一个任务表并将我需要执行的存储过程插入其中(在您的情况下,这将是您的触发器的代码)。然后你可以安排一个作业每隔 X 分钟检查一次这个表并执行其中的代码。
猜你喜欢
  • 2020-05-20
  • 1970-01-01
  • 2011-08-31
  • 1970-01-01
  • 2020-07-24
  • 2019-03-25
  • 2018-07-13
  • 2017-10-31
  • 1970-01-01
相关资源
最近更新 更多