Comment vider le tampon PRINT dans TSQL?

220

J'ai une procédure stockée très longue dans SQL Server 2005 que j'essaie de déboguer, et j'utilise la commande «imprimer» pour le faire. Le problème est que je ne récupère les messages que de SQL Server à la toute fin de mon sproc - j'aimerais pouvoir vider le tampon de messages et voir ces messages immédiatement pendant l'exécution du sproc, plutôt qu'à l'extrême fin.

Erik Forbes
la source
1
Juste un court préavis pour les personnes qui (comme moi) penseront que les réponses ne fonctionnent pas pour elles: assurez-vous de passer à l'onglet "Messages" lorsque la requête est en cours d'exécution. Par défaut, vous verrez l'onglet "Résultats".
Tomasz Gandor

Réponses:

305

Utilisez la RAISERRORfonction:

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

Vous ne devez pas remplacer complètement toutes vos impressions par raiserror. Si vous avez une boucle ou un gros curseur quelque part, faites-le une ou deux fois par itération ou même toutes les plusieurs itérations.

Aussi: J'ai d'abord entendu parler de RAISERROR sur ce lien, que je considère maintenant comme la source définitive de la gestion des erreurs SQL Server et qui vaut vraiment la peine d'être lu:
http://www.sommarskog.se/error-handling-I.html

Joel Coehoorn
la source
41
Notez que TRY / CATCH dans SQL ne détectera que les erreurs de gravité> 10, donc l'utilisation de RAISERROR de cette manière ne sautera pas dans votre instruction CATCH. Ce qui est génial, car cela signifie que vous pouvez toujours utiliser RAISERROR comme celui-ci avec TRY / CATCH. ref: msdn.microsoft.com/en-us/library/ms175976.aspx
Rory
13
Notez que cela ne fonctionne pas après les 500 premiers messages; une fois que vous imprimez plus que cela, la mise en mémoire tampon commence soudainement!
GendoIkari
@MahmoudMoravej Non, j'exécute toujours des processus de longue durée en utilisant RAISEERROR, et je ne fais que gérer le fait qu'après un certain temps, les messages commencent à être mis en mémoire tampon. Il semble que la seule solution serait d'utiliser un autre outil que SSMS.
GendoIkari
1
Je pense que c'est quelque chose qui a changé dans une version récente de SS. Il y a très longtemps, lorsque j'ai écrit ceci, nous avons utilisé RAISERROR pour une journalisation approfondie des processus par lots de nuit avec plus de 500 messages, et ce n'était pas un problème. Mais beaucoup de choses peuvent changer en 7 ans.
Joel Coehoorn
1
À l'avis de @ GendoIkari. Je l'ai essayé avec ssms de 2016SP1 avec ce script. À 500, il passe en tampon de 50 lignes et à 1k, il passe à 100 lignes chacun. Cela a continué au moins jusqu'à 2k, mais j'ai arrêté le script. declare @i int set @i = 0 declare @t varchar (100) tandis que 1 = 1 begin set @i = @i + 1 set @t = 'print' + convert (varchar, @i) RAISERROR (@t, 10 , 1) AVEC NOWAIT waitfor delay '00: 00: 00.010 'end
Zartag
28

En s'appuyant sur la réponse de @JoelCoehoorn, mon approche consiste à laisser toutes mes instructions PRINT en place et à les suivre simplement avec l'instruction RAISERROR pour provoquer le vidage.

Par exemple:

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

L'avantage de cette approche est que les instructions PRINT peuvent concaténer des chaînes, contrairement à RAISERROR. (Donc, dans les deux cas, vous avez le même nombre de lignes de code, car vous devez déclarer et définir une variable à utiliser dans RAISERROR).

Si, comme moi, vous utilisez AutoHotKey ou SSMSBoost ou un outil équivalent, vous pouvez facilement configurer un raccourci tel que "] flush" pour entrer la ligne RAISERROR pour vous. Cela vous fait gagner du temps s'il s'agit de la même ligne de code à chaque fois, c'est-à-dire qu'il n'a pas besoin d'être personnalisé pour contenir du texte spécifique ou une variable.

Mike
la source
6
Notez que cela RAISERROR()prend en charge l' printf()interpolation de chaîne de style. Par exemple, si @MyVariableNameest un type de stringish (par exemple VARCHAR(MAX), NVARCHAR(MAX)etc.), vous pouvez utiliser RAISERROR()avec une seule ligne: RAISERROR(N'MyVariableName: %s', 0, 1, @MyVariableName).
binki
C'est tellement pratique! Je sais que RAISERROR peut effectuer une substitution simple, mais essayez de remplacer une heure [date] ou d'appeler une fonction à l'intérieur de l'instruction RAISERROR! Cette réponse vous donne un FLUSH simple sous la forme d'une erreur vide (au prix d'une nouvelle ligne).
Tomasz Gandor
19

Oui ... Le premier paramètre de la fonction RAISERROR a besoin d'une variable NVARCHAR. Essayez donc ce qui suit;

-- 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
la source
10
Regardez l'onglet Messages en bas, à côté de l'onglet Résultats ou passez en mode Résultats en texte.
Mehmet Ergut
Pour passer en mode Résultats en mode Texte, dans SSMS, menu Outils -> Options -> Résultats de la requête -> SQL Server -> Général -> Destination par défaut pour les résultats, et choisissez "Résultats en texte" au lieu de "Résultats en grilles", re -Ouvrez la fenêtre de requête et vous ne resterez pas là à regarder un onglet de résultats vierge comme un mannequin pendant que la sortie RAISERROR va à l'onglet Messages.
Adam
12

Une autre meilleure option est de ne pas dépendre de PRINT ou RAISERROR et de simplement charger vos instructions "print" dans une table ## Temp dans TempDB ou une table permanente dans votre base de données qui vous donnera une visibilité immédiate des données via une instruction SELECT à partir d'une autre fenêtre . Cela fonctionne le mieux pour moi. L'utilisation d'une table permanente sert également de journal à ce qui s'est passé dans le passé. Les instructions d'impression sont pratiques pour les erreurs, mais en utilisant la table des journaux, vous pouvez également déterminer le point d'échec exact en fonction de la dernière valeur enregistrée pour cette exécution particulière (en supposant que vous suiviez l'heure de début d'exécution globale dans votre table des journaux.)

Eric Isaacs
la source
2
Cela peut être un problème si vous écrivez un script vraiment transactionnel avec validation et annulation. Je ne pense pas que vous pourrez interroger votre table temporaire en direct - et elle disparaîtra si votre transaction échoue.
SteveJ
@SteveJ vous pouvez l'interroger en direct en utilisant SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;dans votre session de surveillance
TheConstructor
1
@TheConstructor; C'est un conseil utile - je m'en servirai, merci. Cependant, ne nous reste-t-il pas encore avec la table temporaire disparaissant lors de la restauration? Si vous effectuez une analyse des défaillances, il semble que ce serait une grande lacune.
SteveJ
1
@SteveJ oui, il y a certainement ceci. Vous pouvez bien sûr copier les données d'une READ UNCOMMITTEDtransaction dans une autre table, mais vous manquez probablement l'instant juste avant ROLLBACK. Donc, cela résout probablement le «jusqu'où? pas le "pourquoi le retour en arrière?"
TheConstructor
4

Juste pour la référence, si vous travaillez dans des scripts (traitement par lots), pas dans une procédure stockée , la sortie de vidage est déclenchée par la commande GO, par exemple

print 'test'
print 'test'
go

En général, ma conclusion est la suivante: la sortie de l'exécution du script mssql, exécutée dans l'interface graphique SMS ou avec sqlcmd.exe, est vidée dans le fichier, stdoutput, la fenêtre gui lors de la première instruction GO ou jusqu'à la fin du script.

Le vidage à l'intérieur des procédures stockées fonctionne différemment, car vous ne pouvez pas placer GO à l'intérieur.

Référence: déclaration tsql Go

Robert Lujo
la source
2
gone se contente pas de vider la sortie, il termine le lot selon le lien que vous avez fourni. Tout ce que vous avez declaresupprimé est donc peu utilisable pour le débogage. declare @test int print "I want to read this!" go set @test=5sera bien que vous une revendication d'erreur @testn'est pas définie car elle est dans un nouveau lot.
asontu
1
Je suis d'accord, ce n'est pas la bonne réponse à cette question, mais j'ai mis la réponse (voir l'avertissement au début) car cela pourrait être utile pour quelqu'un d'autre - par exemple, quelqu'un qui exécute batch sql.
Robert Lujo