Мне нужно вернуть частичный результат (как простой выбор) из хранимой процедуры, прежде чем она будет завершена.
Возможно ли это сделать?
Если да, то как это сделать?
Если нет, то какой обходной путь?
РЕДАКТИРОВАТЬ: У меня есть несколько частей процедуры. В первой части я вычисляю несколько строк. Я использую их позже в процедуре для выполнения дополнительных операций. Проблема в том, что строка нужна вызывающей стороне как можно скорее. Поэтому мне нужно вычислить эту строку и передать ее обратно (каким-то образом из выбора, например), а затем продолжить работу. Звонящий получает свою ценную строку гораздо быстрее.
Абонент - это веб-сервис.
sql-server-2012
t-sql
stored-procedures
multi-thread
web-service
Богдан Богданов
источник
источник
Ответы:
Вы, вероятно, ищете
RAISERROR
команду сNOWAIT
опцией.По замечаниям :
Это не возвращает результаты из
SELECT
оператора, но позволит вам передавать сообщения / строки обратно клиенту. Если вы хотите быстро вернуть подмножество данных, которые вы выбираете, вы можете рассмотретьFAST
подсказку запроса.Добавил Шеннон Северанс в комментарии:
Из Обработки ошибок и транзакций в SQL Server Эрланда Соммарского:
Смотрите исходную статью для полного контекста.
источник
FAST
Я решил эту проблему для меня, когда мне нужно было синхронизировать выполнение хранимой процедуры и кода C #, чтобы усугубить и воспроизвести состояние гонки. Проще потреблять наборы результатов программно, чем использовать что-то вродеRAISERROR()
. Когда я начал читать ваш ответ, мне показалось, что вы говорили, что это невозможно сделатьSELECT
, так что, возможно, это можно уточнить?ОБНОВЛЕНИЕ: См. Ответ Струцкого ( выше ) и комментарии по крайней мере для одного примера, где это не ведет себя так, как я ожидаю, и опишите здесь. Мне придется экспериментировать / читать дальше, чтобы обновить мое понимание, когда позволит время ...
Если ваш абонент взаимодействует с базой данных асинхронно или является многопоточным или многопоточным, так что вы можете открыть второй сеанс, пока первый еще выполняется, вы можете создать таблицу для хранения частичных данных и обновить их по мере выполнения процедуры. Затем это может быть прочитано вторым сеансом с установленным уровнем изоляции транзакции 1, чтобы позволить ему читать незафиксированные изменения:
1: согласно комментариям и последующему обновлению в ответе srutzky, установка уровня изоляции не требуется, если отслеживаемый процесс не заключен в транзакцию, хотя я склонен устанавливать его по привычке в таких обстоятельствах, поскольку это не вызывает вред, когда не требуется в этих случаях
Конечно, если у вас может быть несколько процессов, работающих таким образом (что, вероятно, если ваш веб-сервер принимает одновременных пользователей, и это очень редко случается), вам необходимо каким-то образом идентифицировать информацию о ходе этого процесса. , Возможно, передайте процедуру свежеиспеченного UUID в качестве ключа, добавьте это в таблицу прогресса и прочитайте с:
Я использовал этот метод для мониторинга длительных ручных процессов в SSMS. Я не могу решить, пахнет ли он слишком сильно для меня, чтобы рассмотреть его использование в производстве ...
источник
OP уже попытался отправить несколько наборов результатов (не MARS) и увидел, что он действительно ожидает завершения хранимой процедуры, прежде чем возвращать какие-либо наборы результатов. Учитывая эту ситуацию, вот несколько вариантов:
Если ваши данные достаточно малы, чтобы поместиться в пределах 128 байт, вы, скорее всего, можете использовать
SET CONTEXT_INFO
их, чтобы сделать это значение видимым черезSELECT [context_info] FROM [sys].[dm_exec_requests] WHERE [session_id] = @SessionID;
. Вам просто нужно выполнить быстрый запрос перед запуском хранимой процедурыSELECT @@SPID;
и получить его черезSqlCommand.ExecuteScalar
.Я только что проверил это, и это работает.
Аналогично предложению @ David для помещения данных в таблицу «прогресса», но без необходимости возиться с проблемами очистки или параллелизма / разделения процессов:
Guid
код приложения и передайте его в качестве параметра хранимой процедуре. Сохраните этот Guid в переменной, так как он будет использоваться несколько раз.CREATE TABLE ##MyProcess_{GuidFromApp};
. Таблица может иметь любые столбцы с любыми типами данных, которые вам нужны.Когда у вас есть данные, вставьте их в эту глобальную временную таблицу.
В коде приложения, начинают пытаться читать данные, но обернуть
SELECT
вIF EXISTS
так что не будет ошибкой , если таблица не была создана еще:С
String.Format()
, вы можете заменить{0}
на значение в переменной Guid. Проверьте, еслиReader.HasRows
и если true, то прочитайте результаты, иначе позвонитеThread.Sleep()
или как угодно, чтобы потом опросить снова.Льготы:
EXEC
/sp_executesql
)Я проверил это, и он работает как ожидалось. Вы можете попробовать это сами с помощью следующего примера кода.
На одной вкладке запроса выполните следующее, а затем выделите 3 строки в блок-комментарии и выполните это:
Перейдите на вкладку «Сообщения» и скопируйте напечатанный GUID. Затем откройте другую вкладку запроса и выполните следующую команду, поместив GUID, скопированный с вкладки «Сообщения другого сеанса», в инициализацию переменной в строке 1:
Продолжай бить F5. Вы должны увидеть 1 запись в течение первых 10 секунд, а затем 2 записи в течение следующих 10 секунд.
Вы можете использовать SQLCLR для обратного вызова вашего приложения через веб-сервис или каким-либо другим способом.
Вы могли бы , возможно , использовать
PRINT
/RAISERROR(..., 1, 10) WITH NOWAIT
передать строки обратно сразу, но это будет немного сложнее из - за следующие проблемы:VARCHAR(8000)
илиNVARCHAR(4000)
Сообщения по умолчанию также не отправляются, пока процесс не завершится. Однако это поведение можно изменить, установив для свойства SqlConnection.FireInfoMessageEventOnUserErrors значение
true
. В документации говорится:Недостатком здесь является то, что большинство ошибок SQL больше не будет вызывать
SqlException
. В этом случае вам необходимо протестировать дополнительные свойства событий, которые передаются в обработчик событий сообщений. Это справедливо для всего соединения, что делает вещи немного сложнее, но не управляемыми.Все сообщения отображаются на одном уровне без отдельного поля или свойства, чтобы отличить одно от другого. Порядок, в котором они получены, должен совпадать с порядком их отправки, но не уверен, достаточно ли это надежно. Возможно, вам понадобится добавить тег или что-то, что вы сможете затем проанализировать. Таким образом, вы, по крайней мере, можете быть уверены, какой из них есть какой.
источник
RETURN
утверждения). Так что это не работает.SqlConnection
? Сколько данных вы хотите передать обратно? Какие типы данных? Вы пробовалиPRINT
илиRAISERROR WITH NOWAIT
?Если ваша хранимая процедура должна выполняться в фоновом режиме (то есть асинхронно), вам следует использовать Service Broker. Это немного затрудняет настройку, но как только вы это сделаете, вы сможете запустить хранимую процедуру (неблокирующую) и прослушивать сообщения о ходе выполнения столько времени, сколько захотите.
источник