我在SQL Server 2005中有一个非常长时间运行的存储过程,我正在尝试调试,我正在使用'print'命令来执行它。问题是,我只是在我的sproc的最后从SQL Server得到消息-我希望能够在sproc的运行时期间立即刷新消息缓冲区并看到这些消息,而不是在最后。


当前回答

为了扩展Eric Isaac的回答,以下是如何正确使用表格方法:

首先,如果你的sp使用事务,你将无法实时监控表的内容,除非你使用READ UNCOMMITTED选项:

SELECT *
FROM table_log WITH (READUNCOMMITTED);

or

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

注意这些重要的细节:

关闭xact_abort;防止SQL Server只是关闭整个事务,而不是运行你的catch块,如果你使用这种技术,一定要包括它。 使用@table_variable,而不是#temp_table。临时表也会受到回滚的影响。

其他回答

为了扩展Eric Isaac的回答,以下是如何正确使用表格方法:

首先,如果你的sp使用事务,你将无法实时监控表的内容,除非你使用READ UNCOMMITTED选项:

SELECT *
FROM table_log WITH (READUNCOMMITTED);

or

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

注意这些重要的细节:

关闭xact_abort;防止SQL Server只是关闭整个事务,而不是运行你的catch块,如果你使用这种技术,一定要包括它。 使用@table_variable,而不是#temp_table。临时表也会受到回滚的影响。

根据@JoelCoehoorn的回答,我的方法是保留所有的PRINT语句,然后简单地在它们后面加上RAISERROR语句来引起flush。

例如:

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

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

如果像我一样,你使用AutoHotKey或SSMSBoost或等效的工具,你可以很容易地设置一个快捷键,如“]flush”,为你进入RAISERROR行。如果每次都是同一行代码,这可以节省时间,也就是说,不需要定制以保存特定的文本或变量。

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

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

仅供参考,如果您在脚本(批处理)中工作,而不是在存储过程中,刷新输出是由GO命令触发的,例如。

print 'test'
print 'test'
go

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

存储过程内部的刷新功能不同,因为您不能将GO放在存储过程内部。

参考:tsql Go语句