Могу ли я создать одноразовую функцию в скрипте или хранимой процедуре?

109

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

Просто любопытно.

Марк Карпентер
источник
возможно, есть лучший способ делать то, что вы хотите, без функции. возможно, вам стоит опубликовать фрагмент кода, который вы хотите превратить в функцию?
DForck42
вы генерируете функцию динамически, чтобы она каждый раз была другой? если ваша функция всегда одинакова, просто оставьте это в базе данных
KM.
1
Я пытался сделать это, чтобы запрос был более читабельным. Идею создания огромных запросов сложно поддерживать.
Jp_ 07

Ответы:

66

Вы можете позвонить CREATE Functionв начале вашего сценария и DROP Functionближе к концу.

Джоэл Кохорн
источник
6
Я собирался это предложить. Просто будьте осторожны, чтобы ваш скрипт завершился; если он прервется, у вас все еще будет функция в БД.
chocojosh
6
Вы можете выполнять проверку IF EXISTS перед каждым запуском и удалять, если что-то найдено.
Адриан Годонг,
7
@chocojosh, это должно быть нормально, если вы заключите его в транзакцию. Функция не должна быть в базе данных, если транзакция взорвалась.
Джефф ЛаФэй
12
@JoelCoehoorn: для этого по-прежнему требуются права записи.
user2284570
2
Обратите внимание, что это не будет работать внутри функции - временные функции внутри функций не разрешены. См .: technet.microsoft.com/en-us/library/ms191320.aspx#Restrictions
Дэниел Нил,
95

Вы можете создавать временные хранимые процедуры, например:

create procedure #mytemp as
begin
   select getdate() into #mytemptable;
end

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

Рон Сэвидж
источник
7
Это должен быть ответ. Это действительно одноразовое использование, если только временная область подключения (одиночный #), и преимущество обхода ограничений пользователя sql.
Тодд
Как тогда его используют? Разве это не опечатка в имени процедуры, использованной в выражении select into?
jgomo3 08
Я в состоянии получить результаты из примера хранимой процедуры , когда я удалить BEGINключевое слово, и заменить ENDключевое слово с GO.
Джозеф Дикстра
OP запрашивал временную ФУНКЦИЮ, и по крайней мере SQL Server 2012 не разрешает # -синтаксис для функций. Только процедуры.
Erk
Это не функция сценария, и для этого могут потребоваться разрешения. Чтобы избежать повторяющихся сегментов, единственный вариант SQL - это оператор WITH.
alex.peter 06
25

Общие табличные выражения позволяют вам определять, что по сути является представлениями, которые сохраняются только в рамках ваших операторов select, insert, update и delete. В зависимости от того, что вам нужно сделать, они могут быть ужасно полезными.

Велбог
источник
5
Это следует принять как правильный ответ. Принятый ответ не является потокобезопасным.
kalyan 02
11
Зависит от того, что вы пытаетесь сделать. Я нашел этот вопрос, потому что я пишу сеялку данных и не хочу повторять 10 строк MERGE INTO 30 раз. Меня не волнует потокобезопасность, и CTE мне не подходят.
солипсик
16
Я думаю, что этот ответ и утверждения о том, что это правильный ответ, упускают из виду, что вопрос ищет временную ФУНКЦИЮ, а не временную ТАБЛИЦУ. Если я чего-то не упускаю (что не редкость), CTE сопоставимы с временными таблицами.
JD Long
8
Функция может принимать аргументы, а CTE - нет.
Рэзван Флавиус Панда
4
Есть много различий между CTE и временной хранимой процедурой (это правильный ответ здесь, IMO). Во-первых, CTE существуют только для одного оператора, тогда как временные переменные можно использовать во всем скрипте. Среди других отличий: (1) CTE не могут содержать ту же логику, что и SP, (2) CTE не могут принимать переменные. CTE - это просто синтаксический сахар, который позволяет вам более легко создавать вложенные табличные выражения для использования в операторе. Даже в этом случае они могут быть опасными с точки зрения производительности, если вы не знаете о предостережениях.
Lopsided
12

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

DECLARE @add_a_b_func nvarchar(4000) = N'SELECT @c = @a + @b;';
DECLARE @add_a_b_parm nvarchar(500) = N'@a int, @b int, @c int OUTPUT';

DECLARE @result int;
EXEC sp_executesql @add_a_b_func, @add_a_b_parm, 2, 3, @c = @result OUTPUT;
PRINT CONVERT(varchar, @result); -- prints '5'
Tmdean
источник
4

В сценариях у вас больше возможностей и больше возможностей для рациональной декомпозиции. Посмотрите на режим SQLCMD (меню запроса -> режим SQLCMD), в частности, на команды: setvar и: r.

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

create proc DoStuff
as begin

  declare @sql nvarchar(max)

  /*
  define function here, within a string
  note the underscore prefix, a good convention for user-defined temporary objects
  */
  set @sql = '
    create function dbo._object_name_twopart (@object_id int)
    returns nvarchar(517) as
    begin
      return 
        quotename(object_schema_name(@object_id))+N''.''+
        quotename(object_name(@object_id))
    end
  '

  /*
  create the function by executing the string, with a conditional object drop upfront
  */
  if object_id('dbo._object_name_twopart') is not null drop function _object_name_twopart
  exec (@sql)

  /*
  use the function in a query
  */
  select object_id, dbo._object_name_twopart(object_id) 
  from sys.objects
  where type = 'U'

  /*
  clean up
  */
  drop function _object_name_twopart

end
go

Это приближается к глобальной временной функции, если такая существует. Он по-прежнему виден другим пользователям. Вы можете добавить @@ SPID вашего соединения, чтобы сделать имя уникальным, но тогда в остальной части процедуры также потребуется использовать динамический SQL.

Питер Радоккья
источник
3

Ниже приводится то, что я использовал в прошлом, чтобы удовлетворить потребность в скалярной UDF в MS SQL:

IF OBJECT_ID('tempdb..##fn_Divide') IS NOT NULL DROP PROCEDURE ##fn_Divide
GO
CREATE PROCEDURE ##fn_Divide (@Numerator Real, @Denominator Real) AS
BEGIN
    SELECT Division =
        CASE WHEN @Denominator != 0 AND @Denominator is NOT NULL AND  @Numerator != 0 AND @Numerator is NOT NULL THEN
        @Numerator / @Denominator
        ELSE
            0
        END
    RETURN
END
GO

Exec ##fn_Divide 6,4

Этот подход, который использует глобальную переменную для ПРОЦЕДУРЫ, позволяет вам использовать функцию не только в ваших сценариях, но и в ваших потребностях динамического SQL.

Грегори Харт
источник