Чи можу я створити функцію одноразового використання у сценарії чи збереженій процедурі?


109

Чи існує в SQL Server 2005 концепція одноразового використання або локальної функції, оголошеної всередині SQL-скрипту чи збереженої процедури? Я хотів би відмовитись від складності сценарію, який я пишу, але це вимагатиме можливості оголосити функцію.

Просто цікаво.


можливо, є кращий спосіб робити те, що ви хочете, без функції. можливо, ви повинні опублікувати фрагмент коду, який ви хочете перетворити на функцію?
DForck42

чи ви генеруєте функцію динамічно, тому вона щоразу відрізняється? якщо функція завжди однакова, просто залиште її в базі даних
KM.

1
Я намагався зробити це як спосіб зробити запит більш читабельним. Ідею створення величезних запитів дуже важко підтримувати.
Jp_

Відповіді:


65

Ви можете зателефонувати CREATE Functionблизько початку сценарію та DROP Functionдо кінця.


6
Я збирався запропонувати це. Будьте обережні, щоб ваш сценарій закінчувався; якщо вона перерве, ви все одно будете мати функцію в БД.
chocojosh

6
Ви можете виконати перевірку IF EXISTS перед кожним запуском та видалити, якщо щось знайдено.
Адріан Годонг

7
@chocojosh, це повинно бути нормально, якщо ви завершите транзакцію. Функція не повинна знаходитися в базі даних, якщо транзакція бомбує.
Jeff LaFay

12
@JoelCoehoorn: для цього все ж потрібні привілеї для запису.
користувач2284570

2
Зауважте, що це не працює у функції - тимчасові функції всередині функцій заборонені. Дивіться: technet.microsoft.com/en-us/library/ms191320.aspx#Restrictions
Daniel Neel

95

Ви можете створити тимчасові збережені процедури, такі як:

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

у сценарії SQL, але не функціонує. Можливо, ви зможете запустити додаток зберігання даних у тимчасовій таблиці, а потім скористайтеся цією інформацією пізніше у сценарії.


7
Це має бути відповіддю. Це справді одноразове використання, якщо тільки з'єднання охоплює тимчасовий (одиночний номер #) і має перевагу обійти обмеження користувачем sql.
Тодд

Як він використовується тоді? Хіба це не помилка в назві процедури, яка використовується у виразі select у виразі?
jgomo3

Я можу отримати результати вашої прикладної збереженої процедури, коли я видаляю BEGINключове слово та замінюю ENDключове слово GO.
Джозеф Дикстра

ОП просила тимчасову ФУНКЦІЮ, і принаймні SQL-сервер 2012 не дозволить # -синтаксис функцій. Тільки процедури.
Ерк

Це не є функцією в сценарії, але може вимагати дозволів. Щоб уникнути повторюваних сегментів, єдиний параметр, який має SQL, є оператором WITH.
alex.peter

25

Загальні табличні вирази дозволяють вам визначити, що по суті є переглядами, які тривають лише в межах вашого вибору, вставляти, оновлювати та видаляти заяви. Залежно від того, що вам потрібно зробити, вони можуть бути дуже корисними.


5
Це слід сприймати як правильну відповідь. Прийнята відповідь не є безпечною для потоків.
калян

11
Залежить, що ти намагаєшся зробити. Я знайшов це запитання, оскільки я пишу інструмент для розробки даних, і я не хочу повторювати 10 рядків MERGE INTO 30 разів. Мене не хвилює безпека ниток, і CTE не працюватимуть на мене.
solipsicle

16
Я думаю, що ця відповідь і твердження про те, що це правильна відповідь, пропускають, що питання шукає тимчасову ФУНКЦІЮ, а не темп ТАБЛИЦІ. Якщо я щось не пропускаю (не рідкість), CTE порівнянні з тимчасовими таблицями.
JD Long

8
Функція може приймати аргументи, тоді як CTE не може.
Пазва Развана Флавія

4
Існує багато відмінностей між CTE і тимчасово збереженою процедурою (що є правильною відповіддю тут IMO). Для початку CTE існують лише для одного оператора, тоді як змінні temp можуть використовуватися у всьому сценарії. Інші відмінності включають: (1) CTE не можуть використовувати ту саму логіку, що і SP, (2) CTE не можуть приймати змінні. CTE - це просто синтаксичний цукор, який дозволяє вам легше будувати вкладені вирази таблиці для використання в операторі. Навіть тоді вони можуть бути небезпечними для виконання, якщо ви не знаєте про застереження.
однобокий

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.