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


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'

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.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.