我在SQL Server 2005中有一个非常长时间运行的存储过程,我正在尝试调试,而且我正在使用'print'命令来执行此操作。问题是,我只是在我的sproc的最后从SQL Server获取消息 - 我希望能够刷新消息缓冲区并在sproc的运行时立即看到这些消息,而不是在端。
答案 0 :(得分:288)
使用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
答案 1 :(得分:17)
是...... RAISERROR函数的第一个参数需要一个NVARCHAR变量。所以尝试以下方法;
-- Replace PRINT function
DECLARE @strMsg NVARCHAR(100)
SELECT @strMsg = 'Here''s your message...'
RAISERROR (@strMsg, 0, 1) WITH NOWAIT
OR
RAISERROR (n'Here''s your message...', 0, 1) WITH NOWAIT
答案 2 :(得分:17)
在@JoelCoehoorn的答案的基础上,我的方法是保留所有PRINT语句,并使用RAISERROR语句跟随它们以引起刷新。
例如:
PRINT 'MyVariableName: ' + @MyVariableName
RAISERROR(N'', 0, 1) WITH NOWAIT
这种方法的优点是PRINT语句可以连接字符串,而RAISERROR则不能。 (因此,无论哪种方式,您都拥有相同数量的代码行,因为您必须声明并设置要在RAISERROR中使用的变量)。
如果像我一样使用AutoHotKey或SSMSBoost或同等工具,您可以轻松设置快捷方式,例如“] flush”,为您输入RAISERROR行。如果每次都是相同的代码行,则可以节省您的时间,即不需要自定义以保存特定文本或变量。
答案 3 :(得分:11)
另一个更好的选择是不依赖于PRINT或RAISERROR而只是加载你的" print"将语句转换为TempDB中的## Temp表或数据库中的永久表,这将使您可以通过另一个窗口的SELECT语句立即查看数据。这对我来说是最好的。使用永久表也可以作为过去发生的事情的日志。 print语句对于错误很方便,但是使用日志表还可以根据特定执行的最后记录值确定确切的故障点(假设您跟踪日志表中的总执行开始时间。)
答案 4 :(得分:4)
仅供参考,如果您使用脚本(批处理),而不是存储过程,则GO命令会触发刷新输出,例如
import java.util.Scanner;
public class Email_Validation {
public static void main(String[] args) {
// User-input code
Scanner scan = new Scanner(System.in);
System.out.println("Enter your email address");
String email = scan.next();
// Code to check if email ends with '.' (period sign)
boolean checkEndDot = false;
checkEndDot = email.endsWith(".");
// Code to find out last index of '@' sign
int indexOfAt = email.indexOf('@');
int lastIndexOfAt = email.lastIndexOf('.');
//Code to check occurence of @ in the email address
int countOfAt = 0;
for (int i = 0; i < email.length(); i++) {
if(email.charAt(i)=='@')
countOfAt++; }
// Code to check occurence of [period sign i..e, "."] after @
String buffering = email.substring(email.indexOf('@')+1, email.length());
int len = buffering.length();
int countOfDotAfterAt = 0;
for (int i=0; i < len; i++) {
if(buffering.charAt(i)=='.')
countOfDotAfterAt++; }
// Code to print userName & domainName
String userName = email.substring(0, email.indexOf('@'));
String domainName = email.substring(email.indexOf('@')+1, email.length());
System.out.println("\n");
if ((countOfAt==1) && (userName.endsWith(".")==false) && (countOfDotAfterAt ==1) &&
((indexOfAt+3) <= (lastIndexOfAt) && !checkEndDot)) {
System.out.println("\"Valid email address\"");}
else {
System.out.println("\n\"Invalid email address\""); }
System.out.println("\n");
System.out.println("User name: " +userName+ "\n" + "Domain name: " +domainName);
}
}
一般来说,我的结论是:mssql脚本执行的输出,在SMS GUI或sqlcmd.exe中执行,在第一个GO语句或直到脚本结束时被刷新到文件,stdoutput,gui窗口。
以不同方式刷新存储过程函数内部,因为您无法将GO放入其中。
答案 5 :(得分:0)
为了扩展Eric Isaac's answer,以下是正确使用表格方法的方法:
首先,如果您的 sp 使用事务,您将无法实时监控表的内容,除非您使用 READ UNCOMMITTED
选项:
SELECT *
FROM table_log WITH (READUNCOMMITTED);
或
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT *
FROM table_log;
为了解决回滚问题,在日志表上放置一个递增的ID,并使用以下代码:
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
。临时表也会受到回滚的影响。