如何在TSQL中刷新PRINT缓冲区?


220

我在SQL Server 2005中有一个非常长时间运行的存储过程,正在尝试调试,并且正在使用“ print”命令来进行调试。问题是,我只能在存储过程的最后从SQL Server取回消息-我希望能够刷新消息缓冲区并在存储过程的运行时立即查看这些消息,而不是在运行时结束。


1
只是对那些像我一样认为答案对他们不起作用的人的简短通知:请确保在运行查询时切换到“消息”选项卡。默认情况下,您会看到“结果”标签。
Tomasz Gandor

Answers:


305

使用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


41
请注意,SQL中的TRY / CATCH将仅捕获严重性> 10的错误,因此以这种方式使用RAISERROR不会跳入CATCH语句。太好了,因为这意味着您仍然可以在TRY / CATCH中使用RAISERROR。参考:msdn.microsoft.com/en-us/library/ms175976.aspx
Rory

13
请注意,这在前500条消息之后无效。一旦您打印了更多的东西,它就会突然开始缓冲!
GendoIkari 2015年

@MahmoudMoravej不,我仍在使用RAISEERROR运行长时间运行的进程,只是处理一段时间后消息开始被缓冲的事实。看来唯一的解决方案是使用SSMS以外的其他工具。
GendoIkari

1
我认为这是在最新版本的SS中发生的变化。早在我第一次写这篇文章的时候,我们就使用RAISERROR广泛记录了包含500多个消息的通宵批处理过程,这不是问题。但是在7年内会有很多变化。
Joel Coehoorn 2015年

1
在@GendoIkari的通知下。我已经使用此脚本在2016SP1中使用ssms进行了尝试。在500时,它切换到缓冲50条线,在1k时,每条切换到100条线。这种情况至少持续到2k,但随后我停止了脚本。声明@i int设置@i = 0声明@t varchar(100)而1 = 1开始设置@i = @i + 1设置@t ='print'+ convert(varchar,@i)RAISERROR(@t,10 ,1)WITH NOWAIT waitwait delay '00:00:00.010'end
Zartag '17

28

在@JoelCoehoorn回答的基础上,我的方法是将所有PRINT语句保留在原位,并在它们后面加上RAISERROR语句以引起刷新。

例如:

PRINT 'MyVariableName: ' + @MyVariableName
RAISERROR(N'', 0, 1) WITH NOWAIT

这种方法的优点是PRINT语句可以连接字符串,而RAISERROR不能。(因此,无论哪种方式,您都有相同数量的代码行,因为您必须声明并设置要在RAISERROR中使用的变量)。

如果像我一样使用AutoHotKey或SSMSBoost或等效工具,则可以轻松设置快捷方式,例如“] flush”,为您输入RAISERROR行。如果每次都是同一行代码,则可以节省您的时间,即不需要自定义以容纳特定的文本或变量。


6
请注意,RAISERROR()它确实支持printf()-style字符串插值。例如,如果@MyVariableName是一个stringish类型(例如,VARCHAR(MAX)NVARCHAR(MAX)等等),可以使用RAISERROR()与一个行:RAISERROR(N'MyVariableName: %s', 0, 1, @MyVariableName)
宾基

真方便!我知道RAISERROR可以做一些简单的替换,但是尝试替换[date] time或从RAISERROR语句内部调用函数!该答案以引发空错误的形式(以换行符为代价)为您提供了简单的FLUSH。
Tomasz Gandor

19

是的... 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

10
查看底部“结果”选项卡旁边的“消息”选项卡,或切换到“结果转为文本”模式。
Mehmet Ergut 2011年

要切换到“结果转换为文本”模式,请在SSMS中,选择“工具”->“选项”->“查询结果”->“ SQL Server”->“常规”->“结果的默认目标”,然后选择“结果转换为文本”,而不是“结果转换为网格”, -打开查询窗口,然后您将不会坐在那里看着空白的“结果”选项卡(如虚拟对象),而RAISERROR输出将进入“消息”选项卡。
亚当

12

另一个更好的选择是不依赖PRINT或RAISERROR,而只是将您的“ print”语句加载到TempDB中的## Temp表或数据库中的永久表中,这将使您可以立即通过另一个窗口中的SELECT语句查看数据。这对我来说是最好的。然后,使用永久表还可以作为过去发生的情况的日志。打印语句很容易出错,但是使用日志表,您还可以基于特定执行的最后一个记录值来确定确切的故障点(假设您在日志表中跟踪总体执行开始时间)。


2
如果您要编写具有提交和回滚功能的真正的事务脚本,则可能会遇到问题。我不相信您将能够实时查询您的临时表-如果您的事务失败,它将消失。
SteveJ,2016年

@SteveJ,您可以SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;在监视会话中使用它实时查询它
TheConstructor 2016年

1
@TheConstructor; 这是个有用的提示-谢谢,我会用到的。但是,难道我们还没有离开临时表进行回滚吗?如果进行故障分析,那似乎是一个很大的缺点。
SteveJ,2013年

1
@SteveJ是的,肯定有这个。您当然可以将READ UNCOMMITTED事务中的数据复制到另一个表,但是您可能会错过之前的时刻ROLLBACK。因此它可能解决了“多远?” 不是“为什么回滚?”
TheConstructor

4

仅供参考,如果您使用脚本(批处理),而不是存储过程,则GO命令触发刷新输出,例如

print 'test'
print 'test'
go

总的来说,我的结论如下:在SMS GUI或sqlcmd.exe中执行的mssql脚本执行输出,将刷新到第一个GO语句上的文件,stdoutput,gui窗口,或者直到脚本结束。

由于无法将GO放入内部,因此刷新存储过程内部的功能有所不同。

参考:tsql Go语句


2
go不仅刷新输出,还按照您提供的链接结束批处理。您的所有内容都会declare被丢弃,因此调试时不太有用。declare @test int print "I want to read this!" go set @test=5尽管您会声称错误声明@test是未定义的,因为它在新批次中。
asontu

1
我同意,这不是此问题的正确答案,但我给出了答案(请参见开始处的免责声明),因为它可能对其他人(例如运行批处理sql的人)有用。
罗伯特·卢霍
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.