我有一个在 SQL Server 2005 中非常耗时的存储过程需要进行调试,我正在使用 'print' 命令来实现。问题是,我只能在 sproc 最后获取来自 SQL Server 的消息 - 我想要刷新消息缓冲区并在 sproc 运行期间立即查看这些消息,而不是在最后。
我有一个在 SQL Server 2005 中非常耗时的存储过程需要进行调试,我正在使用 'print' 命令来实现。问题是,我只能在 sproc 最后获取来自 SQL Server 的消息 - 我想要刷新消息缓冲区并在 sproc 运行期间立即查看这些消息,而不是在最后。
使用RAISERROR
函数:
RAISERROR( 'This message will show up right away...',0,1) WITH NOWAIT
不应将所有的输出都完全替换为RAISERROR。 如果您在循环或大型游标中使用,只需在每次迭代中执行一到两次甚至几次即可。
此外:我最初是在此链接中了解到RAISERROR的,我现在认为这是SQL Server错误处理的权威来源,绝对值得一读:
http://www.sommarskog.se/error-handling-I.html
在 @JoelCoehoorn 的回答基础上,我的做法是保留所有的 PRINT 语句,并在它们后面添加 RAISERROR 语句来进行刷新。
例如:
PRINT 'MyVariableName: ' + @MyVariableName
RAISERROR(N'', 0, 1) WITH NOWAIT
这种方法的优点在于,PRINT语句可以连接字符串,而RAISERROR则不行。(因此无论哪种方式,您都需要相同行数的代码,因为您必须声明和设置一个变量以在RAISERROR中使用)。如果您像我一样使用AutoHotKey或SSMSBoost或等效工具,则可以轻松设置快捷方式,例如“]flush”来为您输入RAISERROR行。 如果每次都是相同的代码行,即不需要自定义以包含特定文本或变量,则这将节省您的时间。RAISERROR()
支持 printf()
风格的字符串插值。例如,如果 @MyVariableName
是字符串类型(例如 VARCHAR(MAX)
、NVARCHAR(MAX)
等),您可以使用一行代码的 RAISERROR()
:RAISERROR(N'MyVariableName: %s', 0, 1, @MyVariableName)
。 - binki是的... RAISERROR函数的第一个参数需要一个NVARCHAR变量。因此,请尝试以下操作;
-- Replace PRINT function
DECLARE @strMsg NVARCHAR(100)
SELECT @strMsg = 'Here''s your message...'
RAISERROR (@strMsg, 0, 1) WITH NOWAIT
或
RAISERROR (n'Here''s your message...', 0, 1) WITH NOWAIT
另一个更好的选择是不依赖于PRINT或RAISERROR,而是将你的“打印”语句加载到TempDB中的##Temp表或数据库中的永久表中,这将通过从另一个窗口的SELECT语句立即让你看到数据。对我来说这是最好的方法。使用永久表还可以作为记录过去发生情况的日志。打印语句对于错误很方便,但使用日志表还可以根据该特定执行的最后一个记录值确定失败点(假设在日志表中跟踪了总体执行开始时间)。
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
实时查询它。 - TheConstructorREAD UNCOMMITTED
事务中将数据复制到另一个表中,但你可能会错过ROLLBACK
之前的那一刻。所以它可能只解决了“到哪里?”而不是“为什么回滚?”的问题。 - TheConstructorSET XACT_ABORT OFF;
并在 catch 块或其他错误检测手段中手动回滚,则可以通过表变量保存日志以避免回滚(请确保使用表变量,因为它们不受事务回滚的影响,但临时表会受到影响):`
-- 在开头
DECLARE @maxLogId INT = (SELECT MAX(ID) FROM LogTable);-- 执行操作-- 错误处理
DECLARE @tmpLog TABLE (/* 日志表列 */);
INSERT INTO @tmpLog
SELECT * FROM LogTable
WHERE ID > @maxLogId;ROLLBACK TRAN;-- 设置标识插入并重新插入 tmpLog 的内容
` - sisisisi仅供参考,如果您在脚本(批处理)中工作,而不是存储过程,则刷新输出是由GO命令触发的,例如:
print 'test'
print 'test'
go
go
不仅会刷新输出,还会结束当前批处理。任何你声明的变量都会被丢弃,因此不适用于调试。例如,declare @test int print "I want to read this!" go set @test=5
会抛出一个错误,表示 @test
未定义,因为它在一个新的批处理中。 - asontu为了扩展Eric Isaac的回答,以下是正确使用表格方法的步骤:
首先,如果您的存储过程使用事务,则无法实时监视表格内容,除非您使用READ UNCOMMITTED
选项:
SELECT *
FROM table_log WITH (READUNCOMMITTED);
或者
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT *
FROM table_log;
SET XACT_ABORT OFF;
BEGIN TRY
BEGIN TRANSACTION mytran;
-- already committed logs are not affected by a potential rollback
-- so only save logs created in this transaction
DECLARE @max_log_id = (SELECT MAX(ID) FROM table_log);
/*
* do stuff, log the stuff
*/
COMMIT TRANSACTION mytran;
END TRY
BEGIN CATCH
DECLARE @log_table_saverollback TABLE
(
ID INT,
Msg NVARCHAR(1024),
LogTime DATETIME
);
INSERT INTO @log_table_saverollback(ID, Msg, LogTime)
SELECT ID, Msg, LogTime
FROM table_log
WHERE ID > @max_log_id;
ROLLBACK TRANSACTION mytran; -- this deletes new log entries from the log table
SET IDENTITY_INSERT table_log ON;
INSERT INTO table_log(ID, Msg, LogTime)
SELECT ID, Msg, LogTime
FROM @log_table_saverollback;
SET IDENTITY_INSERT table_log OFF;
END CATCH
请注意以下重要细节:
SET XACT_ABORT OFF;
防止SQL Server在运行catch块之前直接关闭整个事务,请始终包括它,如果您使用此技术。@table_variable
,而不是#temp_table
。 临时表也会受到回滚的影响。