Можем ли мы написать вспомогательную функцию или процедуру внутри другой хранимой процедуры - SQL Server

Я хочу проверить, имеет ли SQL Server (2000/05/08) возможность записи вложенной хранимой процедуры, что я имел в виду - ПИСЬМО вспомогательной функции/процедуры внутри другой хранимой процедуры. Не называть другой SP.

Почему я думал об этом? Один из моих SP имеет повторяющиеся строки кода и специфичен только для этого SP.So, если у нас есть эта вложенная функция SP, тогда я могу объявить другую суб-локальную процедуру внутри моего основного SP и поместить в него все повторяющиеся строки. и я могу назвать это местным sp в моем основном SP. Я помню, что такая функция доступна в Oracle SP.

Если SQL-сервер также имеет эту функцию, может кто-то объяснить некоторые подробности об этом или предоставить ссылку, где я могу найти документацию.

Спасибо заранее Sai

Ответ 1

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

Ответ 2

Oracle PL/SQL - это особый случай, являющийся языком, основанный на Ada, а не простой DML с некоторыми процедурными конструкциями. Считаете ли вы, что это хорошая идея, вероятно, зависит от вашего аппетита к процедурному кодексу в вашей СУБД и вашей симпатии к изучению сложных новых языков.

Идея подпрограммы, чтобы уменьшить дублирование или иначе, во многом отличается от других платформ баз данных в моем опыте (Oracle, MS SQL, Sybase, MySQL, SQLite в основном).

В то время как proc SQL-building будет работать, я думаю, что Джон прямо говорит о том, что вы не используете его иначе-правильный ответ!

Вы не говорите, в какой форме повторяются ваши повторяющиеся строки, поэтому я предлагаю три потенциальных альтернативы, начиная с самых простых:

  • Ничего не делай. Принять этот процедурный SQL - это примитивный язык, так много "существенных" конструкций, которые вы не использовали бы его вообще, если бы он был не единственным вариантом.

  • Переместите свои процедурные операции за пределы СУБД и выполните их в коде, написанном на более сложном языке. Подумайте, как настроить вашу архитектуру для извлечения бизнес-логики с платформы хранения данных (эй, почему бы не перепроектировать все это!)

  • Если повторение происходит в DML, в частности, SELECT, рассмотрите возможность представления представлений для уменьшения запросов.

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

Это четыре. Я думал о другом, когда печатал; считайте это бонусом.

Ответ 3

Я не рекомендую это делать, поскольку каждый раз, когда он создается, должен быть рассчитан новый план выполнения, но ДА, это определенно может быть сделано (все возможно, но не всегда рекомендуется).

Вот пример:

CREATE PROC [dbo].[sp_helloworld]
AS
BEGIN
    SELECT 'Hello World'
    DECLARE @sSQL VARCHAR(1000)
    SET @sSQL = 'CREATE PROC [dbo].[sp_helloworld2]
            AS
            BEGIN
                SELECT ''Hello World 2''
            END'
    EXEC (@sSQL)

    EXEC [sp_helloworld2];
    DROP PROC [sp_helloworld2];
END

Вы получите предупреждение

The module 'sp_helloworld' depends on the missing object 'sp_helloworld2'.
The module will still be created; however, it cannot run successfully until
the object exists.

Вы можете обойти это предупреждение, используя EXEC ('sp_helloworld2') выше.

Но если вы вызовете EXEC [sp_helloworld], вы получите результаты

Hello World
Hello World 2

Ответ 4

У меня была аналогичная ситуация в SQL Trigger (похожая на SQL-процедуру), где я в основном имел такой же оператор insert, который должен выполняться максимум 13 раз для 13 возможных значений ключа, которые привели к 1 событию. Я использовал счетчик, зацикливал его 13 раз, используя DO WHILE и использовал CASE для каждой обработки ключевых значений, сохраняя флаг, чтобы выяснить, когда мне нужно вставить и когда пропустить.

Ответ 5

CREATE TABLE #t1 (digit INT, name NVARCHAR(10));  
GO

CREATE PROCEDURE #insert_to_t1  
(  
    @digit INT  
,    @name NVARCHAR(10)  
)  
AS  
BEGIN  
    merge #t1 AS tgt  
    using (SELECT @digit, @name) AS src (digit,name)  
    ON    (tgt.digit = src.digit)  
    WHEN matched THEN  
          UPDATE SET name = src.name  
    WHEN NOT matched THEN  
          INSERT (digit,name) VALUES (src.digit,src.name);  
END;  
GO  


EXEC #insert_to_t1 1,'One';  
EXEC #insert_to_t1 2,'Two';  
EXEC #insert_to_t1 3,'Three';  
EXEC #insert_to_t1 4,'Not Four';  
EXEC #insert_to_t1 4,'Four'; --update previous record!  


SELECT    * FROM #t1;

То, что мы делаем здесь, - это создание процедуры, которая живет для жизни соединения и которая впоследствии используется для вставки некоторых данных в таблицу.

Ответ 6

Я согласен с andynormancx, что, похоже, не так много смысла в этом.

Если вы действительно хотите, чтобы общий код содержался внутри SP, вы могли бы, вероятно, сжечь что-то вместе с GOTO или динамическим SQL, но делать это правильно с помощью отдельного SP или UDF было бы лучше почти во всех отношениях.

Ответ 7

John sp_helloworld действительно работает, но здесь причина, по которой вы не видите этого чаще.

При хранении хранимой процедуры очень сильно влияет производительность. Существует статья Microsoft об устранении проблем с производительностью, вызванных большим количеством перекомпиляций, потому что это очень замедляет работу вашей системы:

http://support.microsoft.com/kb/243586

Вместо создания хранимой процедуры вам лучше создать строковую переменную с T-SQL, которую вы хотите вызвать, а затем повторно выполнить эту строковую переменную.

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

Ответ 8

было бы очень приятно, если MS разрабатывает GOSUB помимо GOTO, это легко сделать!

Создание процедур или функций для структуры объектов-объектов "внутренние процедуры".

Я "реализую" это так:

Body1:

goto HEADER HEADER_RET1:

вставить в тело...

goto BODY1_RET

Body2:

goto HEADER HEADER_RET2:

ВСТАВЬТЕ В тело...

goto BODY2_RET

ЗАГОЛОВОК:

Вставить в заголовок

if @fork = 1 goto HEADER_RET1

если @fork = 2 goto HEADER_RET2

выберите 1/0 - проверка потока!

Ответ 9

Спасибо всем за ваши ответы! Мне лучше создать еще один SP с повторяющимся кодом и назвать это, что является наилучшим способом выполнения промежуточных результатов и выглядеть мудрым.

Ответ 10

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

Вдоль линий

select name, userID, fnCaseCount(userID), fnRefCount(UserID)
  from table1 t1
  left join table2 t2
    on t1.userID = t2.UserID

Для относительно небольшого набора (400 пользователей) он вызывал каждую из двух функций один раз. В общей сложности 800 вызовов из хранимой процедуры. Не очень, но никто не ожидал, что сервер sql будет иметь проблемы с этими несколькими вызовами.

Это заняло более 4 минут.

Индивидуально, вызов функции был почти мгновенным. Даже 800 мгновенных вызовов должны быть почти мгновенными.

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

Я скопировал код из функции и поместил его в запрос SQL в хранимой процедуре. Но, похоже, переход между sp и функцией - это то, что поглотило время.

Время выполнения все еще слишком велико на 18 секунд, но позволяет завершить запрос в течение нашего 30-секундного тайм-аута.

Если бы у меня была подпрограмма, это сделало бы код более красивым, но все-таки может добавить дополнительные служебные данные.

Затем я могу попытаться переместить одну и ту же функциональность в представление, которое я могу использовать в соединении.

select t1.UserID, t2.name, v1.CaseCount, v2.RefCount
  from table1 t1
  left join table2 t2
    on t1.userID = t2.UserID
  left join vwCaseCount v1
    on v1.UserID = t1.UserID
  left join vwRefCount v2
    on v2.UserID = t1.UserID

Хорошо, я только что создал представления из функций, поэтому время выполнения выполнялось от 4 минут, до 18 секунд, до 8 секунд. Я буду продолжать играть с ним.