You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何捕获存储过程中执行的Update/Delete语句及受影响行数?

好问题!你想要捕获存储过程里每条Update/Delete的执行情况和受影响行数,这个需求很常见,但直接用你写的那个函数可能会遇到一些坑——比如sys.dm_exec_requests只能返回当前正在执行的请求,等你在函数里调用它时,存储过程里的DML语句可能已经执行完毕,根本不在当前请求列表里了。我来给你分享几个更靠谱的实现方案:

方案1:在存储过程内直接记录(最直接可控)

既然你已经知道存储过程里只有Update和Delete,那最简单的方式就是在每条DML语句执行后,立刻用@@ROWCOUNT获取受影响行数,然后把语句内容和行数插入到专门的跟踪表中。

首先创建一个用于存储跟踪数据的表:

CREATE TABLE dbo.QueryTracking (
    TrackingID INT IDENTITY(1,1) PRIMARY KEY,
    ExecutionTime DATETIME2(3) DEFAULT SYSUTCDATETIME(),
    QueryText NVARCHAR(MAX) NOT NULL,
    AffectedRows INT NOT NULL,
    SessionID INT DEFAULT @@SPID,
    ProcedureName NVARCHAR(128) DEFAULT OBJECT_NAME(@@PROCID)
);

然后修改你的存储过程,给每条Update/Delete加上记录逻辑:

CREATE PROCEDURE dbo.YourTargetProcedure
AS
BEGIN
    SET NOCOUNT ON;

    -- 示例Delete语句
    DELETE FROM dbo.YourTable WHERE Status = 'Expired';
    -- 记录这条Delete操作
    INSERT INTO dbo.QueryTracking (QueryText, AffectedRows)
    VALUES ('DELETE FROM dbo.YourTable WHERE Status = ''Expired'';', @@ROWCOUNT);

    -- 示例Update语句
    UPDATE dbo.AnotherTable SET LastUpdated = GETDATE() WHERE ID BETWEEN 1 AND 100;
    -- 记录这条Update操作
    INSERT INTO dbo.QueryTracking (QueryText, AffectedRows)
    VALUES ('UPDATE dbo.AnotherTable SET LastUpdated = GETDATE() WHERE ID BETWEEN 1 AND 100;', @@ROWCOUNT);
END

这个方案的优点是逻辑清晰、数据准确,完全不需要依赖动态管理视图或其他复杂工具;缺点是需要手动修改每个目标存储过程,适合存储过程数量不多的场景。

方案2:用扩展事件无侵入捕获(无需修改存储过程)

如果不想改动现有存储过程,扩展事件是最佳选择——它是SQL Server原生的轻量级监控工具,性能开销极低,能精准捕获你需要的信息。

步骤1:创建扩展事件会话

CREATE EVENT SESSION TrackDMLInStoredProcedures ON SERVER 
ADD EVENT sqlserver.sql_statement_completed(
    ACTION(
        sqlserver.session_id,
        sqlserver.sql_text,
        sqlserver.object_name
    )
    WHERE (
        sqlserver.is_system = 0 -- 排除系统操作
        AND (statement_type = 'UPDATE' OR statement_type = 'DELETE') -- 只抓Update/Delete
        AND object_type = 'P' -- 只捕获存储过程中的语句
    )
)
ADD TARGET package0.event_file(SET filename=N'C:\SQLLogs\TrackDMLInSP.xel') -- 替换成你的存储路径
WITH (STARTUP_STATE=OFF); -- 可选:设置为ON则SQL服务启动时自动开启会话

步骤2:启动会话

ALTER EVENT SESSION TrackDMLInStoredProcedures ON SERVER STATE = START;

步骤3:查询捕获到的数据

SELECT
    event_data.value('(event/@timestamp)[1]', 'DATETIME2(3)') AS ExecutionTime,
    event_data.value('(event/action[@name="object_name"]/value)[1]', 'NVARCHAR(128)') AS ProcedureName,
    event_data.value('(event/action[@name="sql_text"]/value)[1]', 'NVARCHAR(MAX)') AS QueryText,
    event_data.value('(event/data[@name="row_count"]/value)[1]', 'INT') AS AffectedRows,
    event_data.value('(event/action[@name="session_id"]/value)[1]', 'INT') AS SessionID
FROM (
    SELECT CAST(event_data AS XML) AS event_data
    FROM sys.fn_xe_file_target_read_file('C:\SQLLogs\TrackDMLInSP*.xel', NULL, NULL, NULL)
) AS x
ORDER BY ExecutionTime DESC;

这个方案的优点是完全不需要修改业务代码,能捕获所有存储过程里的DML操作;缺点是需要服务器级权限来创建扩展事件,且需要定期清理生成的事件文件。

关于你写的函数的局限性

你尝试用sys.dm_exec_requests来获取查询文本的思路是对的,但这个视图只能返回当前正在执行的请求。当你在存储过程里调用这个函数时,你拿到的只会是存储过程本身的执行语句,而不是里面已经执行完的Update/Delete。另外,标量函数中调用动态管理视图可能会带来不必要的性能开销,不太适合用来做这类跟踪需求。

总结一下:如果能修改存储过程,优先选方案1;如果不能改代码,就用方案2的扩展事件——这两个方案都比用函数的方式更可靠。

内容的提问来源于stack exchange,提问作者Kiran Dhamale

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 07:49:25