Выполнение хранимой процедуры из функции

Я знаю, что об этом просили до смерти, и я знаю, почему SQL Server не позволяет вам это сделать.

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

И, пожалуйста, не говорите мне преобразовать мою функцию в процедуру...

Итак, что я действительно спрашиваю: есть ли ЛЮБОЙ способ запустить хранимую процедуру из функции?

ИЗМЕНИТЬ:

Доказано: это можно обойти, но это настолько НЕПРАВИЛЬНО, что я бы не стал этого делать. Я собираюсь изменить его на хранимую процедуру и выполнить в другом месте.


person Smur    schedule 14.06.2011    source источник
comment
+1 за вопрос, так как общий совет против этого может послужить предупреждением для кого-то еще, кто думал об этом.   -  person Tom Chantler    schedule 14.06.2011
comment
@Smur: есть еще один способ, который я описываю в своем ответе ниже, который, хотя и потенциально проблематичен при выполнении в многострочном контексте, гораздо менее неправильный, чем использование xp_cmdshell для вызова osql (что действительно должно в любом случае будет SQLCMD ;-).   -  person Solomon Rutzky    schedule 12.08.2015


Ответы (5)


РЕДАКТИРОВАТЬ: Я не пробовал это, поэтому я не могу ручаться за это! И вы уже знаете, что не должны этого делать, поэтому, пожалуйста, не делайте этого. НО...

Попробуйте поискать здесь: http://sqlblog.com/blogs/denis_gobo/archive/2008/05/08/6703.aspx

Ключевым битом является этот бит, который я попытался настроить для ваших целей:

DECLARE @SQL varchar(500)

SELECT @SQL = 'osql -S' +@@servername +' -E -q "exec dbName..sprocName "'

EXEC master..xp_cmdshell @SQL
person Tom Chantler    schedule 14.06.2011
comment
Да, я знаю, что не должен... и я предполагаю, что не буду. Еще я забыл про cmdshell. Я где-то читал об его использовании... В любом случае, большое спасибо. - person Smur; 14.06.2011
comment
Часть меня пускала слюни, когда я это читал. В то время как гораздо большая часть меня кричала от ужаса и бегала по комнате, щебеча, как цыпленок... Серьезно, не ищите хаков, которые позволяют функции иметь побочные эффекты, если вы хотите иметь удобную, дружелюбную среду для любого дольше, чем, скажем, завтра... - person MatBailie; 14.06.2011
comment
@Dems: я полностью с тобой согласен. За подобные действия (кроме академических упражнений) вас, скорее всего, уволят! - person Tom Chantler; 14.06.2011
comment
Если ОП сделает это, ты хочешь, чтобы Меня уволили!? Это так жестоко ;( - person MatBailie; 14.06.2011
comment
@Dems Я действительно смеялся над тем, как сильно ты это подчеркивал ... И успокойся, я знаю, что не должен этого делать, и я не буду. Но держу пари, вы знаете, как это поддерживать приложения. - person Smur; 14.06.2011
comment
Успокойтесь, ребята, я не буду этого делать. А теперь я даже подумываю удалить вопрос, ха-ха. - person Smur; 14.06.2011
comment
Мне? Спокойствие!? Я спокоен! Смотри, я почти не дрожу... Что значит чирикать как Цыпленок? Это не я говорю вам! Это был не я! ПЕРЕСТАНЬТЕ НА МЕНЯ ТАК ПОЗНАВАТЬ!!! - person MatBailie; 14.06.2011
comment
@Dems: не волнуйся, люди несут ответственность за свои действия. Если только вы не думали, что вас следует уволить за то, что вы бегаете по комнате и кричите, как цыпленок! - person Tom Chantler; 14.06.2011
comment
Ржу не могу. Должен ли я сохранить его или удалить его? - person Smur; 14.06.2011
comment
Если это можно выразить в виде вопроса и ответить на него ответом, я бы сказал, что его можно оставить здесь... - person MatBailie; 14.06.2011
comment
Плюс люди будут смеяться над этими комментариями. Сохраняя это. - person Smur; 14.06.2011
comment
Это может работать для выполнения хранимых процедур, но если вы попытаетесь зафиксировать результаты, сгенерированные хранимой процедурой, в операторе вставки, вы получите следующую ошибку: Недопустимое использование побочного оператора «INSERT EXEC» внутри функции. - person Jimbo; 19.10.2018

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

Хранимые процедуры есть.

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


Итак, извините, но нет, вы не можете вызвать хранимую процедуру из функции.

person MatBailie    schedule 14.06.2011
comment
Вашему ответу не хватает объяснения. Суть в том, что почему функции запрещены, вы просто перефразируете одно и то же предложение в трех разных формах. Если речь идет о ссылочной прозрачности, то укажите это. Если речь идет об эффективности, укажите это, но не оставляйте людей в недоумении. - person Luka Ramishvili; 10.05.2012
comment
@LukaRamishvili - я бы немного вам противоречил. Smoking causes cancer because smoking is carcenogenic действительно круглый. Но Functions can't call StoredProcedures because Functions are not allowed to cause side-effects не круглый. Может быть дополнительный вопрос Why can't FN's cause side-effects?, но это действительно дополнительный вопрос. Why? можно задать практически любому уровню, в конце подробно описав дизайн и реализацию оптимизатора. По-моему, это не тот форум. - person MatBailie; 10.05.2012
comment
Не будем об этом спорить, вы правы. ОП не спросил, почему, и ваш ответ был прямым - нет. Но это звучит как доктрина о том, что функции [T-SQL] не имеют побочных эффектов. Важно прояснить, что его ограничивает, причину, почему в первую очередь функциям не разрешено иметь побочные эффекты, а также объяснить, почему не разрешены storprocs. - person Luka Ramishvili; 10.05.2012
comment
@LukaRamishvili Вероятно, причина в том, что точное количество казней и время казней зависят от плана выполнения. Один и тот же запрос может иметь разное количество выполнений функций в зависимости от того, были ли они оценены, например, до или после фильтра. Это может даже варьироваться в зависимости от того же плана для холодного и теплого кеша. пример - person Martin Smith; 08.12.2014
comment
Этот ответ категорически неверен, учитывая 3 способа сделать это, показанные в других ответах. Независимо от того, следует ли это делать, это зависит от контекста (т. е. выполняется ли функция один раз, как в SET @Variable = function, или, возможно, много раз из-за того, что она является частью запроса, который видит много возможных строк, даже если он возвращает только одну строку). Кроме того, неверно, что если функция вызывает хранимую процедуру, функция может иметь побочные эффекты, поскольку SQL Server может предотвратить только эти запрещенные действия, как это происходит при вызове процедуры из функции SQLCLR. - person Solomon Rutzky; 12.08.2015

Другим вариантом, помимо использования OPENQUERY и xp_cmdshell, является использование SQLCLR (функция SQL Server «Интеграция с CLR»). Вариант SQLCLR не только более безопасен, чем два других метода, но и дает потенциальную выгоду от возможности вызывать хранимую процедуру в текущем сеансе, чтобы она имела доступ к любому сеансу. на основе объектов или настроек, таких как:

  • временные таблицы
  • временные хранимые процедуры
  • CONTEXT_INFO

Этого можно добиться, используя «context connection = true;» как ConnectionString. Просто имейте в виду, что все другие ограничения, наложенные на определяемые пользователем функции T-SQL, будут применяться (т. е. не могут иметь побочных эффектов).

Если вы используете обычное соединение (т.е. не используете контекстное соединение), то оно будет работать как независимый вызов, как и при использовании методов OPENQUERY и xp_cmdshell.

ОДНАКО имейте в виду, что если вы будете использовать функцию, которая вызывает хранимую процедуру (независимо от того, какой из 3 отмеченных методов вы используете) в операторе, который влияет более чем на 1 строку, то нельзя ожидать, что поведение будет запускаться один раз для каждой строки. Как упоминал @MartinSmith в комментарии к ответу @MatBailie, оптимизатор запросов не гарантирует ни время, ни количество выполнений функций. Но если вы используете его в операторе SET @Variable = function(); или запросе SELECT * FROM function();, то все должно быть в порядке.

Пример использования определяемой пользователем функции .NET/C# SQLCLR для выполнения хранимой процедуры показан в следующей статье (которую я написал):

Путь к SQLCLR уровня 2: пример хранимой процедуры и функции

person Solomon Rutzky    schedule 20.05.2015

Вот еще один возможный обходной путь:

if exists (select * from master..sysservers where srvname = 'loopback')
    exec sp_dropserver 'loopback'
go
exec sp_addlinkedserver @server = N'loopback', @srvproduct = N'', @provider = N'SQLOLEDB', @datasrc = @@servername
go

create function testit()
    returns int
as
begin
    declare @res int;
    select @res=count(*) from openquery(loopback, 'exec sp_who');
    return @res
end
go

select dbo.testit()

Это не так страшно, как xp_cmdshell, но также имеет слишком много значений для практического использования.

person Vadzim    schedule 26.06.2014
comment
Как бы вы добавили параметр к этому. - person Siyabonga Dube; 04.04.2021
comment
@SiyabongaDube, ссылка в моем ответе показывает пример. Выдержка: второй параметр OPENQUERY — это запрос, который нужно выполнить на удаленном сервере, и вы можете ожидать, что сможете использовать здесь переменную, но вы не можете. Строка запроса должна быть константой, поскольку SQL Server должен иметь возможность определять форму результирующего набора во время компиляции. Это означает, что как только ваш запрос имеет значение параметра, вам нужно использовать динамический SQL. - person Vadzim; 05.04.2021

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

1. Создайте еще один sproc

CREATE PROCEDURE [dbo].[usp_FunctionBuilder]
DECLARE @outerSql VARCHAR(MAX)
DECLARE @innerSql VARCHAR(MAX)

2. Создайте динамический sql, который вы хотите выполнить в своей функции (пример: вы можете использовать цикл и объединение, вы можете читать в другом sproc, использовать операторы и параметры if для условного sql и т. д.)

SET @innerSql = 'your sql'

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

SET @outerSql = 'CREATE FUNCTION [dbo].[fn_GeneratedFunction] ( @Param varchar(10))
RETURNS TABLE
AS
RETURN
' + @innerSql;


EXEC(@outerSql)

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

Вам нужно будет настроить его под свои нужды (пример: изменение возврата в функции)

person C Rudolph    schedule 28.08.2018