Como liberar o buffer PRINT no TSQL?

220

Eu tenho um procedimento armazenado de execução muito longa no SQL Server 2005 que estou tentando depurar e estou usando o comando 'print' para fazer isso. O problema é que estou recebendo apenas as mensagens do SQL Server no final do meu sproc - eu gostaria de liberar o buffer de mensagens e vê-las imediatamente durante o tempo de execução do sproc, e não no momento fim.

Erik Forbes
fonte
1
Apenas um breve aviso para as pessoas que (como eu) acham que as respostas não funcionam para elas: mude para a guia "Mensagens" quando a consulta estiver em execução. Por padrão, você verá a guia "Resultados".
Tomasz Gandor

Respostas:

305

Use a RAISERRORfunção:

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

Você não deve substituir completamente todas as suas impressões por raiserror. Se você tiver um loop ou cursor grande em algum lugar, faça-o uma ou duas vezes por iteração ou mesmo a cada várias iterações.

Além disso: eu aprendi sobre RAISERROR neste link, que agora considero a fonte definitiva no tratamento de erros do SQL Server e definitivamente vale a pena ler:
http://www.sommarskog.se/error-handling-I.html

Joel Coehoorn
fonte
41
Observe que TRY / CATCH no SQL captura apenas erros com gravidade> 10, portanto, usar RAISERROR dessa maneira não salta para a instrução CATCH. O que é ótimo, pois significa que você ainda pode usar o RAISERROR assim com TRY / CATCH. ref: msdn.microsoft.com/en-us/library/ms175976.aspx
Rory
13
Observe que isso não funciona após as primeiras 500 mensagens; uma vez que você imprime mais do que isso, de repente começa a armazenar em buffer!
GendoIkari 10/09/2015
@MahmoudMoravej Não, ainda estou executando processos de longa execução usando RAISEERROR, e apenas lidando com o fato de que, depois de um tempo, as mensagens começam a ficar em buffer. Parece que a única solução seria usar uma ferramenta diferente do SSMS.
GendoIkari
1
Eu acho que isso é algo que mudou em uma versão recente do SS. No começo, quando escrevi isso, usamos o RAISERROR para o registro extensivo de processos em lote durante a noite com muito mais de 500 mensagens, e isso não foi um problema. Mas muita coisa pode mudar em 7 anos.
Joel Coehoorn
1
No aviso do @ GendoIkari. Eu tentei com ssms de 2016SP1 com este script. Em 500, alterna para o buffer de 50 linhas e em 1k, para 100 linhas cada. Isso continuou pelo menos até 2k, mas então eu parei o script. declare @i int set @i = 0 declare @t varchar (100) enquanto 1 = 1 começa o conjunto @i = @i + 1 set @t = 'print' + converte (varchar, @i) RAISERROR (@t, 10 , 1) COM NOWAIT waitfor delay '00: 00: 00.010 'end
Zartag 8/08/17
28

Com base na resposta de @JoelCoehoorn, minha abordagem é deixar todas as minhas declarações PRINT no lugar e simplesmente segui-las com a declaração RAISERROR para causar o flush.

Por exemplo:

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

A vantagem dessa abordagem é que as instruções PRINT podem concatenar seqüências de caracteres, enquanto o RAISERROR não pode. (Portanto, de qualquer forma, você tem o mesmo número de linhas de código, pois teria que declarar e definir uma variável para usar no RAISERROR).

Se, como eu, você usar o AutoHotKey ou o SSMSBoost ou uma ferramenta equivalente, poderá configurar facilmente um atalho como "] flush" para inserir a linha RAISERROR para você. Isso economiza tempo se for sempre a mesma linha de código, ou seja, não precisar ser personalizado para conter texto ou variável específica.

Mike
fonte
6
Observe que RAISERROR()suporta printf()interpolação de cadeia no estilo. Por exemplo, se @MyVariableNameé um tipo stringish (por exemplo, VARCHAR(MAX), NVARCHAR(MAX), etc.), você pode usar RAISERROR()com uma linha: RAISERROR(N'MyVariableName: %s', 0, 1, @MyVariableName).
binki
Isso é tão conveniente! Eu sei que o RAISERROR pode fazer uma substituição simples, mas tente substituir um [date] time ou chamar uma função de dentro da instrução RAISERROR! Esta resposta fornece um FLUSH simples na forma de gerar um erro vazio (ao custo de uma nova linha).
Tomasz Gandor
19

Sim ... O primeiro parâmetro da função RAISERROR precisa de uma variável NVARCHAR. Então tente o seguinte;

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

OU

RAISERROR (n'Here''s your message...', 0, 1) WITH NOWAIT
tcbrazil
fonte
10
Observe a guia Mensagens na parte inferior, ao lado da guia Resultados ou mude para o modo Resultados em texto.
Mehmet Ergut
Para alternar para o modo Resultados no modo texto, no SSMS, menu Ferramentas -> Opções -> Resultados da consulta -> SQL Server -> Geral -> Destino padrão dos resultados e escolha "Resultados no texto" em vez de "Resultados nas grades". - abra a janela de consulta e você não ficará sentado olhando para uma guia Resultados em branco como um manequim enquanto a saída RAISERROR vai para a guia Mensagens.
Adam
12

Outra opção melhor é não depender de PRINT ou RAISERROR e apenas carregar suas instruções "print" em uma tabela ## Temp no TempDB ou em uma tabela permanente em seu banco de dados que lhe dará visibilidade dos dados imediatamente através de uma instrução SELECT de outra janela . Isso funciona melhor para mim. O uso de uma tabela permanente também serve como um log para o que aconteceu no passado. As instruções de impressão são úteis para erros, mas, usando a tabela de log, você também pode determinar o ponto exato da falha com base no último valor registrado para essa execução específica (supondo que você rastreie o horário de início da execução geral na tabela de log).

Eric Isaacs
fonte
2
Isso pode ser um problema se você estiver escrevendo um script verdadeiramente transacional com confirmação e reversão. Não acredito que você possa consultar sua tabela temporária ao vivo - e ela desaparecerá se sua transação falhar.
SteveJ
@SteveJ, você pode consultá-lo ao vivo usando SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;sua sessão de monitoramento
TheConstructor 7/16/16
1
@TheConstructor; Essa é uma dica útil - eu vou fazer uso disso, obrigado. No entanto, ainda não estamos com a tabela temporária desaparecendo na reversão? Se estiver analisando falhas, parece que isso seria uma grande falha.
SteveJ
1
@ SteveJ sim, certamente existe isso. Obviamente, você pode copiar os dados em uma READ UNCOMMITTEDtransação para outra tabela, mas provavelmente perde o momento imediatamente antes ROLLBACK. Portanto, provavelmente resolve o 'quão longe?' não o 'por que retroceder?'
TheConstructor
4

Apenas para referência, se você trabalha em scripts (processamento em lote), não em procedimento armazenado , a saída de liberação é acionada pelo comando GO, por exemplo

print 'test'
print 'test'
go

Em geral, minha conclusão é a seguinte: a saída da execução do script mssql, executada na GUI do SMS ou com sqlcmd.exe, é liberada para arquivo, stdoutput, janela da GUI na primeira instrução GO ou até o final do script.

A descarga dentro do procedimento armazenado funciona de maneira diferente, pois você não pode colocar o GO dentro.

Referência: instrução tsql Go

Robert Lujo
fonte
2
gonão apenas libera a saída, finaliza o lote conforme o link que você forneceu. Tudo o que você declared é descartado, portanto, não é muito útil para depuração. declare @test int print "I want to read this!" go set @test=5você ainda não conseguiu @testdefinir uma reivindicação de erro porque está em um novo lote.
asontu 21/09/2015
1
Concordo que esta não é a resposta correta para esta pergunta, mas coloquei a resposta (consulte o aviso de isenção no início), pois pode ser útil para outra pessoa - por exemplo, alguém que executa o sql em lote.
Robert Lujo 21/09