Как очистить буфер PRINT в TSQL?

220

У меня есть очень долго выполняемая хранимая процедура в SQL Server 2005, которую я пытаюсь отладить, и для этого я использую команду «печать». Проблема в том, что я получаю сообщения только от SQL Server в самом конце моего sproc - я хотел бы иметь возможность очистить буфер сообщений и видеть эти сообщения сразу во время выполнения sproc, а не в самом конец.

Эрик Форбс
источник
1
Просто короткое уведомление для людей, которые (как и я) думают, что ответы не работают для них: обязательно переключитесь на вкладку «Сообщения» во время выполнения запроса. По умолчанию вы увидите вкладку «Результаты».
Томаш Гандор

Ответы:

305

Используйте 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

Джоэл Коухорн
источник
41
Обратите внимание, что TRY / CATCH в SQL будет перехватывать только ошибки со степенью серьезности> 10, поэтому использование RAISERROR таким образом не приведет к переходу в оператор CATCH. Это здорово, так как это означает, что вы все еще можете использовать RAISERROR вот так с TRY / CATCH. ref: msdn.microsoft.com/en-us/library/ms175976.aspx
Рори,
13
Обратите внимание, что это не работает после первых 500 сообщений; как только вы напечатаете больше, оно внезапно начнет буферизоваться!
GendoIkari
@MahmoudMoravej Нет, я все еще запускаю долго работающие процессы, используя RAISEERROR, и просто имею дело с тем фактом, что через некоторое время сообщения начинают буферизироваться. Похоже, что единственным решением было бы использовать другой инструмент, кроме SSMS.
GendoIkari
1
Я думаю, что это изменилось в последней версии SS. Когда я впервые написал это, мы использовали RAISERROR для расширенной регистрации ночных пакетных процессов со многими более чем 500 сообщениями, и это не было проблемой. Но многое может измениться за 7 лет.
Джоэл Коухорн
1
При уведомлении @ GendoIkari. Я попробовал это с ssms из 2016SP1 с этим сценарием. На 500 он переключается на буферизацию 50 строк, а на 1k он переключается на 100 строк каждая. Это продолжалось как минимум до 2к, но потом я остановил скрипт. объявить @i int set @i = 0 объявить @t varchar (100), а 1 = 1 начать установить @i = @i + 1 установить @t = 'print' + convert (varchar, @i) RAISERROR (@t, 10 , 1) С NOWAIT до конца задержки '00: 00: 00.010 '
Zartag
28

Основываясь на ответе @JoelCoehoorn, мой подход состоит в том, чтобы оставить все мои операторы PRINT на месте и просто следовать им с помощью оператора RAISERROR, чтобы вызвать сброс.

Например:

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

Преимущество этого подхода состоит в том, что операторы PRINT могут объединять строки, а RAISERROR - нет. (Таким образом, в любом случае у вас будет такое же количество строк кода, как и в случае объявления и установки переменной для использования в RAISERROR).

Если, как и я, вы используете AutoHotKey или SSMSBoost или эквивалентный инструмент, вы можете легко настроить ярлык, например "] flush", чтобы ввести для вас строку RAISERROR. Это экономит ваше время, если каждый раз используется одна и та же строка кода, т. Е. Не требует настройки для хранения определенного текста или переменной.

Майк
источник
6
Обратите внимание, что RAISERROR()поддерживается printf()интерполяция строк в стиле. Например, если @MyVariableNameтип stringish (например, VARCHAR(MAX), NVARCHAR(MAX)и т.д.), вы можете использовать RAISERROR()с одной строкой: RAISERROR(N'MyVariableName: %s', 0, 1, @MyVariableName).
Бинки
Это так удобно! Я знаю, что RAISERROR может сделать некоторую простую подстановку, но попробуйте заменить время [date] или вызвать функцию изнутри инструкции RAISERROR! Этот ответ дает вам простой FLUSH в виде появления пустой ошибки (за счет новой строки).
Томаш Гандор
19

Да ... Первый параметр функции RAISERROR нуждается в переменной NVARCHAR. Так что попробуйте следующее;

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

ИЛИ

RAISERROR (n'Here''s your message...', 0, 1) WITH NOWAIT
tcbrazil
источник
10
Посмотрите на вкладку «Сообщения» внизу, рядом с вкладкой «Результаты» или переключитесь в режим «Результаты в текст».
Мехмет Эргут
Чтобы переключиться в режим «Результаты в текст», в SSMS выберите «Сервис» -> «Параметры» -> «Результаты запроса» -> «SQL Server» -> «Общие» -> «Назначение по умолчанию для результатов» и выберите «Результаты в текст» вместо «Результаты в таблицы» - откройте окно запроса, и тогда вы не будете сидеть и смотреть на пустую вкладку «Результаты», как на фиктивную, в то время как выходные данные RAISERROR переходят на вкладку Сообщения.
Адам
12

Другой лучший вариант - не зависеть от PRINT или RAISERROR, а просто загружать ваши операторы «print» в таблицу ## Temp в TempDB или постоянную таблицу в вашей базе данных, которая немедленно предоставит вам видимость данных через оператор SELECT из другого окна. , Это работает лучше всего для меня. Использование постоянной таблицы также служит в качестве журнала того, что произошло в прошлом. Операторы печати удобны для ошибок, но с помощью таблицы журнала вы также можете определить точную точку отказа на основе последнего зарегистрированного значения для этого конкретного выполнения (при условии, что вы отслеживаете общее время начала выполнения в своей таблице журнала).

Эрик Айзекс
источник
2
Это может быть проблемой, если вы пишете подлинно транзакционный скрипт с фиксацией и откатом. Я не верю, что вы сможете запросить вашу временную таблицу в реальном времени - и она исчезнет, ​​если ваша транзакция не удастся.
SteveJ
@SteveJ, вы можете запросить его в режиме реального времени, используя SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;в своем сеансе мониторинга
TheConstructor
1
@TheConstructor; Это полезный совет - я воспользуюсь этим, спасибо. Тем не менее, разве мы не оставили временную таблицу, уходящую при откате? Если проводить анализ сбоев, кажется, что это будет большим недостатком.
SteveJ
1
@ SteveJ да, конечно, есть это. Конечно, вы можете скопировать данные в READ UNCOMMITTEDтранзакции в другую таблицу, но вы, вероятно, упускаете момент прямо перед этим ROLLBACK. Так что это, вероятно, решает, "как далеко?" не "почему откат?"
TheConstructor
4

Просто для справки: если вы работаете в сценариях (пакетная обработка), а не в хранимой процедуре , сброс данных запускается командой GO, например

print 'test'
print 'test'
go

В общем, мой вывод следующий: вывод выполнения скрипта mssql, выполняющегося в графическом интерфейсе SMS или с помощью sqlcmd.exe, сбрасывается в файл, stdoutput, окно графического интерфейса при первом операторе GO или до конца скрипта.

Промывка внутри хранимой процедуры работает иначе, так как вы не можете поместить GO внутрь.

Ссылка: заявление tsql Go

Роберт Лужо
источник
2
goне просто сбрасывает вывод, он завершает пакет согласно предоставленной вами ссылке. Все, что вы делаете, declareотбрасывается, поэтому не очень пригодно для отладки. declare @test int print "I want to read this!" go set @test=5Будет ли, однако, ошибка, требующая @testподтверждения, не определена, поскольку она находится в новой партии.
asontu
1
Я согласен, это неправильный ответ на этот вопрос, но я поставил ответ (см. Заявление об отказе в начале), поскольку он может быть полезен для кого-то другого - например, для кого-то, кто запускает пакетный SQL.
Роберт Лудзё