Пользовательские функции SQL и ветвление хранимых процедур

В настоящее время я работаю над устаревшим приложением и унаследовал от него некоторый теневой SQL. Проект так и не был запущен в производство, но сейчас он в пути. Во время начального тестирования я обнаружил ошибку. Приложение вызывает хранимую процедуру, которая вызывает множество других хранимых процедур, создает курсоры, перебирает курсоры в цикле и многое другое. ФМЛ.

В настоящее время приложение спроектировано таким образом, что оно вызывает хранимую процедуру, а затем перезагружает пользовательский интерфейс с новым набором данных. Конечно, данные, которые мы хотим отобразить, все еще обрабатываются на стороне SQL-сервера, поэтому результаты пользовательского интерфейса не будут полными при отображении. Чтобы исправить это, я просто заморозил поток на 30 секунд перед загрузкой пользовательского интерфейса. Это ужасный хак, и я хотел бы исправить это должным образом на стороне SQL.

Мой вопрос: стоит ли преобразовывать хранимые процедуры ветвления в функции? Заставит ли это основную хранимую процедуру ждать возвращаемого значения перед обработкой?

Вот хранимая процедура:

    ALTER PROCEDURE [dbo].[ALLOCATE_BUDGET] 
    @budget_scenario_id uniqueidentifier
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @constraint_type varchar(25)

    -- get project cache id and constraint type
    SELECT @constraint_type = CONSTRAINT_TYPE
    FROM BUDGET_SCENARIO WHERE BUDGET_SCENARIO_ID = @budget_scenario_id

    -- constraint type is Region by Region
    IF (@constraint_type = 'Region by Region')
      EXEC BUDGET_ALLOCATE_SCENARIO_REGIONBYREGION @budget_scenario_id

    -- constraint type is City Wide
    IF (@constraint_type = 'City Wide')
      EXEC BUDGET_ALLOCATE_SCENARIO_CITYWIDE @budget_scenario_id

    -- constraint type is Do Nothing
    IF (@constraint_type = 'Do Nothing')
      EXEC BUDGET_ALLOCATE_SCENARIO_DONOTHING @budget_scenario_id

    -- constraint type is Unconstrained
    IF (@constraint_type = 'Unconstrained')
      EXEC BUDGET_ALLOCATE_SCENARIO_UNCONSTRAINED @budget_scenario_id

    --set budget scenario status to "Allocated", so reporting tabs in the application are populated
    EXEC BUDGET_UPDATE_SCENARIO_STATUS @budget_scenario_id, 'Allocated'
END

Чтобы избежать отображения неполного набора результатов в пользовательском интерфейсе вызывающего приложения .NET до завершения курсоров в вызовах ветвления, стоит ли преобразовывать эти хранимые процедуры в функции с возвращаемыми значениями? Заставит ли это SQL ожидать завершения основного вызова хранимой процедуры [ALLOCATED_BUDGET]?

  • Последний вызов оператора SQL в хранимой процедуре устанавливает статус «Выделено». Это происходит до завершения обработки курсоров в предыдущих вызовах. Влияет ли преобразование этих вызовов в вызовы функций на то, как хранимая процедура возвращает фокус приложению?

Любая обратная связь очень ценится. У меня есть ощущение, что я прав в отношении функций SQL, но не уверен на 100%.

** Дополнительная информация:

  1. Выполняемый код использует [async=true] в строке подключения.
  2. Выполняемый код использует метод [SqlCommand].[ExecuteNonQuery].

person D3vtr0n    schedule 20.07.2009    source источник


Ответы (6)


Как вы вызываете процедуру? Я предполагаю, что вы используете ExecuteNonQuery() для вызова процедуры. Попробуйте вызвать процедуру с помощью ExecuteScalar() и измените процедуру следующим образом:

ALTER PROCEDURE [dbo].[ALLOCATE_BUDGET] 
    @budget_scenario_id uniqueidentifier
AS
BEGIN
   ...

    RETURN True
END

Это должно привести к тому, что ваш код выполнения данных в .NET будет ждать завершения процедуры, прежде чем продолжить. Если вы не хотите, чтобы ваш пользовательский интерфейс «зависал» во время выполнения процедуры, используйте BackgroundWorkerProcess или что-то подобное для запуска запроса в отдельном потоке и найдите завершенный обратный вызов, чтобы обновить пользовательский интерфейс с результатами.

person Chris Porter    schedule 20.07.2009
comment
Исполняемый код использует метод [SqlCommand].[ExecuteNonQuery]. - person D3vtr0n; 20.07.2009

Вы также можете попробовать использовать оператор RETURN в своих дочерних хранимых процедурах, который можно использовать для возврата кода результата родительской процедуре. Вы можете вызвать дочернюю процедуру чем-то вроде «exec @myresultcode = BUDGET_ALLOCATE_SCENARIO_REGIONBYREGION()». Я думаю, что это должно заставить родительскую процедуру дождаться завершения дочерней процедуры.

person tbreffni    schedule 20.07.2009
comment
так что не имеет значения, если это хранимая процедура, а не функция? функция по определению ВОЗВРАЩАЕТ ЗНАЧЕНИЕ. не безопаснее ли заставить их работать? Я хочу проверить возвращаемое значение, чтобы убедиться, что оно было успешным, а затем обновить свой сценарий_status. Спасибо за помощь. - person D3vtr0n; 20.07.2009
comment
Вам решать, сделаете ли вы их функциями или хранимыми процедурами. Просто имейте в виду, что функции имеют гораздо больше ограничений на то, что они могут делать по сравнению с хранимыми процедурами, например, вы не можете иметь в них операторы обновления. См. эту статью: msdn.microsoft.com/en-us/library/ms187650. aspx для получения дополнительных сведений. Возможно, вам будет проще хранить их как хранимые процедуры. - person tbreffni; 21.07.2009
comment
Я предпочитаю хранимые процедуры пользовательским функциям, но пользовательские функции имеют очень важное преимущество — возможность легко работать с ними внутри SQL Studio, как если бы они были таблицами. Если, например, у вас есть хранимая процедура, которая возвращает огромный набор записей, и вам нужно получить одну запись. Вам нужно будет либо сбросить этот набор записей во временную таблицу и изменить его, либо поместить в Excel и отфильтровать там. С табличной функцией вы можете запустить SELECT * FROM UDF_Table1. Можно использовать как поместив логику в UDF, так и обернув ее в SP. - person Chris Porter; 21.07.2009

Я никогда не слышал, чтобы хранимая процедура могла вернуться к вызывающей стороне, продолжая выполняться в фоновом режиме.

На самом деле, я зайду так далеко, что скажу, что не верю, что это происходит. Если вы видите разницу между пользовательским интерфейсом и тем, что, по вашему мнению, должен был сделать SP, то я полагаю, что это имеет другую причину.

Есть ли в строке подключения async=true? Выполняется ли SP с помощью BeginExecuteReader или Begin-что-либо еще?

person John Saunders    schedule 20.07.2009
comment
Я добавил [async=true] в строку подключения, но все равно получаю те же проблемные результаты. Он выполняется с помощью объекта SQL Command и метода ExecuteNonQuery. - person D3vtr0n; 20.07.2009
comment
Я не упомянул async=true, потому что думал, что это поможет. Если вы не вызываете методы Begin*, это не имеет никакого эффекта. Я все еще не верю, что ваша проблема в том, что SP продолжает выполняться после возврата. Происходит что-то еще. - person John Saunders; 21.07.2009
comment
Ну, очевидно, это курсоры в дочерних процедурах. Они работают, как фоновый процесс, в то время как пользовательский интерфейс обновляется. Что мне нужно, так это лучший контроль над потоком моих возвратов SQL. Это беспорядок, но я готов принять вызов. Спасибо за ваш вклад. - person D3vtr0n; 21.07.2009
comment
Фоновой обработки нет. Если бы курсоры выполняли фоновую обработку, я бы слышал об этом все это время и боялся бы курсоров гораздо больше, чем на самом деле. Идите вперед и избавьтесь от курсоров, но никакая фоновая обработка не будет происходить после вашего возвращения. OTOH, запрос A может запустить SP, затем запрос B может запустить его, и запрос B может все еще выполняться, когда запрос A завершится. - person John Saunders; 21.07.2009

Рискуя показаться слишком простым, я предлагаю вам создать таблицу, в которой можно хранить статус сохраненного процесса. Каким-то образом флаг, который может указывать на завершение выполнения всего процесса и подпроцесса.

Вы можете запросить это из пользовательского интерфейса, чтобы узнать, выполняются ли какие-либо действия, опрашивая этот код состояния.

person shahkalpeshp    schedule 20.07.2009
comment
это звучит как слишком много доступа к данным, на мой вкус. - person D3vtr0n; 20.07.2009

Влияет ли преобразование этих вызовов в вызовы функций на то, как хранимая процедура возвращает фокус приложению?

No.

Хранимая процедура не имеет представления о том, что ее вызывающий объект является приложением пользовательского интерфейса. В хранимой процедуре нет ничего, что могло бы повлиять на поведение приложения пользовательского интерфейса.

Скорее всего, приложение пользовательского интерфейса вызывает хранимую процедуру для одного соединения, а затем обновляет свои данные для другого соединения. Существует множество способов заставить пользовательский интерфейс откладывать обновление, но я настаиваю на том, что должно быть одно соединение с базой данных.

person Amy B    schedule 20.07.2009
comment
Я согласен, я предпочитаю писать приложения с одним подключением к базе данных (singleton). проблема в том, что я не писал этот код. этот код был создан в течение 4 лет, а затем отложен. теперь он вернулся, как плохой фильм ужасов. Моя идея использования функций заключается в том, что функции возвращают значение по определению. Вышеупомянутая хранимая процедура не допускает ветвления или дальнейшей обработки (например, последнего оператора EXEC в хранимой процедуре выше) до тех пор, пока не будет возвращено значение этой функции. или я ошибаюсь? - person D3vtr0n; 20.07.2009

Лично я был бы гораздо больше обеспокоен заменой этих курсоров, чем преобразованием их в функции.

И я бы не запускал последний процесс, пока не проверю действительный код возврата из предыдущих процессов (у этой штуки настоящие проблемы, если один из предыдущих процессов умирает!)

Также подумайте, должно ли все это быть в транзакции (эти процедуры изменяют данные в таблице?)

(Я единственный, кто находит забавным, что у вас есть процедура для запуска процесса для Ничего не делать?)

person HLGEM    schedule 20.07.2009
comment
что посоветуете вместо курсоров? я согласен, я перенесу этот последний оператор EXEC в отдельный вызов из приложения. Мне нравится идея иметь транзакцию, обернутую вокруг этого. - person D3vtr0n; 20.07.2009
comment
Я не могу порекомендовать что-либо вместо курсоров, пока не узнаю, что они делают. Способ замены курсора оператором, основанным на множестве, очень сильно зависит от задачи, которую выполняет курсор. И не все курсоры можно заменить, но возможно более 90%. - person HLGEM; 21.07.2009