¿Cómo elimino el búfer de impresión en TSQL?

220

Tengo un procedimiento almacenado de muy larga duración en SQL Server 2005 que estoy tratando de depurar, y estoy usando el comando 'imprimir' para hacerlo. El problema es que solo recibo los mensajes de SQL Server al final de mi sproc; me gustaría poder vaciar el búfer de mensajes y ver estos mensajes inmediatamente durante el tiempo de ejecución de la sproc, en lugar de hacerlo final.

Erik Forbes
fuente
1
Solo un breve aviso para las personas que (como yo) piensan que las respuestas no les funcionan: asegúrese de cambiar a la pestaña "Mensajes" cuando se ejecuta la consulta. Por defecto, verá la pestaña "Resultados".
Tomasz Gandor

Respuestas:

305

Usa la RAISERRORfunción:

RAISERROR( 'This message will show up right away...',0,1) WITH NOWAIT

No debe reemplazar completamente todas sus impresiones con raiserror. Si tiene un bucle o un cursor grande en alguna parte, hágalo una o dos veces por iteración o incluso cada varias iteraciones.

Además: aprendí por primera vez acerca de RAISERROR en este enlace, que ahora considero la fuente definitiva sobre el manejo de errores de SQL Server y definitivamente vale la pena leerlo:
http://www.sommarskog.se/error-handling-I.html

Joel Coehoorn
fuente
41
Tenga en cuenta que TRY / CATCH en SQL solo detectará errores con gravedad> 10, por lo que usar RAISERROR de esta manera no saltará a su declaración CATCH. Lo cual es genial, ya que significa que aún puede usar RAISERROR de esta manera con TRY / CATCH. ref: msdn.microsoft.com/en-us/library/ms175976.aspx
Rory
13
Tenga en cuenta que esto no funciona después de los primeros 500 mensajes; una vez que imprime más que eso, de repente comienza a almacenarse en el búfer
GendoIkari
@MahmoudMoravej No, todavía estoy ejecutando procesos de larga duración usando RAISEERROR, y solo estoy lidiando con el hecho de que después de un tiempo, los mensajes comienzan a almacenarse en el búfer. Parece que la única solución sería usar una herramienta diferente que no sea SSMS.
GendoIkari
1
Creo que esto es algo que cambió en una versión reciente de SS. Hace mucho tiempo, cuando escribí esto por primera vez, utilizamos RAISERROR para el registro extensivo de procesos por lotes durante la noche con más de 500 mensajes, y no fue un problema. Pero mucho puede cambiar en 7 años.
Joel Coehoorn
1
En el aviso de @ GendoIkari. Lo probé con ssms de 2016SP1 con este script. A 500 cambia a buffering 50 líneas y a 1k cambia a 100 líneas cada una. Esto continuó al menos hasta 2k, pero luego detuve el script. declare @i int set @i = 0 declare @t varchar (100) mientras que 1 = 1 comienza set @i = @i + 1 set @t = 'print' + convert (varchar, @i) RAISERROR (@t, 10 , 1) CON NOWAIT waitfor delay '00: 00: 00.010 'end
Zartag
28

Sobre la base de la respuesta de @JoelCoehoorn, mi enfoque es dejar todas mis declaraciones PRINT en su lugar y simplemente seguirlas con la declaración RAISERROR para causar el vaciado.

Por ejemplo:

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

La ventaja de este enfoque es que las instrucciones PRINT pueden concatenar cadenas, mientras que RAISERROR no. (De cualquier manera, tiene el mismo número de líneas de código, ya que tendría que declarar y establecer una variable para usar en RAISERROR).

Si, como yo, utiliza AutoHotKey o SSMSBoost o una herramienta equivalente, puede configurar fácilmente un acceso directo como "] flush" para ingresar la línea RAISERROR por usted. Esto le ahorra tiempo si es la misma línea de código cada vez, es decir, no necesita ser personalizado para contener texto específico o una variable.

Miguel
fuente
66
Tenga en cuenta que RAISERROR()admite la printf()interpolación de cadenas de estilo. Por ejemplo, si @MyVariableNamees un tipo stringish (por ejemplo, VARCHAR(MAX), NVARCHAR(MAX), etc.), se puede utilizar RAISERROR()con una línea: RAISERROR(N'MyVariableName: %s', 0, 1, @MyVariableName).
binki
¡Esto es muy conveniente! Sé que RAISERROR puede hacer una simple sustitución, ¡pero intente sustituir una [fecha] hora o llamar a una función desde dentro de la declaración RAISERROR! Esta respuesta le proporciona un FLUSH simple en forma de generar un error vacío (a costa de una nueva línea).
Tomasz Gandor
19

Sí ... El primer parámetro de la función RAISERROR necesita una variable NVARCHAR. Entonces intente lo siguiente;

-- Replace PRINT function
DECLARE @strMsg NVARCHAR(100)
SELECT @strMsg = 'Here''s your message...'
RAISERROR (@strMsg, 0, 1) WITH NOWAIT

O

RAISERROR (n'Here''s your message...', 0, 1) WITH NOWAIT
tcbrazil
fuente
10
Mire la pestaña Mensajes en la parte inferior, junto a la pestaña Resultados o cambie al modo Resultados a texto.
Mehmet Ergut
Para cambiar al modo Resultados a Texto, en SSMS, en el menú Herramientas -> Opciones -> Resultados de la consulta -> Servidor SQL -> General -> Destino predeterminado para resultados, y elija "Resultados a texto" en lugar de "Resultados a cuadrículas", vuelva -abra la ventana de consulta y luego no se sentará allí mirando una pestaña de Resultados en blanco como un maniquí mientras la salida RAISERROR va a la pestaña Mensajes.
Adam
12

Otra mejor opción es no depender de PRINT o RAISERROR y simplemente cargar sus declaraciones "print" en una tabla ## Temp en TempDB o una tabla permanente en su base de datos que le dará visibilidad de los datos inmediatamente a través de una declaración SELECT desde otra ventana . Esto funciona mejor para mí. El uso de una tabla permanente también sirve como un registro de lo que sucedió en el pasado. Las declaraciones de impresión son útiles para errores, pero utilizando la tabla de registro también puede determinar el punto exacto de falla en función del último valor registrado para esa ejecución en particular (suponiendo que rastrea el tiempo de inicio de ejecución general en su tabla de registro).

Eric Isaacs
fuente
2
Esto podría ser un problema si está escribiendo un script realmente transaccional con commit y rollback. No creo que pueda consultar su tabla temporal en vivo, y desaparecerá si su transacción falla.
SteveJ
@SteveJ puede consultarlo en vivo utilizando SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;en su sesión de monitoreo
TheConstructor
1
@TheConstructor; Esa es una sugerencia útil. Haré uso de eso, gracias. Sin embargo, ¿no nos queda todavía que la tabla temporal desaparezca en retroceso? Si se realiza un análisis de fallas, parece que sería una gran deficiencia.
SteveJ
1
@ SteveJ sí, ciertamente hay esto. Por supuesto, puede copiar los datos de una READ UNCOMMITTEDtransacción a otra tabla, pero probablemente se pierda el momento justo antes ROLLBACK. Entonces, probablemente resuelva el '¿hasta dónde?' no el '¿por qué retroceder?'
TheConstructor
4

Solo como referencia, si trabaja en scripts (procesamiento por lotes), no en un procedimiento almacenado , el comando GO activa la salida de vaciado, por ejemplo

print 'test'
print 'test'
go

En general, mi conclusión es la siguiente: la salida de la ejecución del script mssql, que se ejecuta en la GUI de SMS o con sqlcmd.exe, se vacía en el archivo, stdoutput, ventana gui en la primera instrucción GO o hasta el final del script.

El vaciado dentro del procedimiento almacenado funciona de manera diferente, ya que no puede colocar GO dentro.

Referencia: instrucción tsql Go

Robert Lujo
fuente
2
gono solo descarga la salida, finaliza el lote según el enlace que proporcionó. Todo lo que declaredeseches se descarta, por lo que no es muy útil para la depuración. declare @test int print "I want to read this!" go set @test=5aunque un error de reclamo @testno está definido porque está en un nuevo lote.
asontu
1
Estoy de acuerdo, esta no es la respuesta adecuada a esta pregunta, pero puse la respuesta (ver descargo de responsabilidad al comienzo) ya que podría ser útil para otra persona, por ejemplo, alguien que ejecuta lotes sql.
Robert Lujo