Можем ли мы написать подфункцию или процедуру внутри другой хранимой процедуры - PullRequest
18 голосов
/ 30 января 2009

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

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

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

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

Ответы [ 10 ]

5 голосов
/ 30 января 2009

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

Вот пример:

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 голосов
/ 10 декабря 2014
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;

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

4 голосов
/ 30 января 2009

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

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

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

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

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

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

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

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

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

4 голосов
/ 30 января 2009

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

3 голосов
/ 24 сентября 2012

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

3 голосов
/ 30 января 2009

Джона sp_helloworld действительно работает, но вот причина, почему вы не видите, что это делается чаще.

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

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

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

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

2 голосов
/ 15 мая 2014

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

По линиям

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 и function - это то, что пожирает время.

Время выполнения все еще слишком велико и составляет 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 секунд. Я буду продолжать играть с этим.

2 голосов
/ 25 марта 2012

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

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

Я "внедряю" это так

Body1:

Перейти к HEADER HEADER_RET1:

insert into body ...

goto BODY1_RET

Body2:

Перейти к HEADER HEADER_RET2:

INSERT INTO body....

goto BODY2_RET

HEADER:

insert into header

if @fork=1 goto HEADER_RET1

if @fork=2 goto HEADER_RET2

select 1/0 --flow check!
1 голос
/ 30 января 2009

Я согласен с andynormancx, что в этом нет особого смысла.

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

0 голосов
/ 30 января 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...