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


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.