Створіть нову функцію за кодом, якщо її немає


15

Я хочу створити нову функцію за сценарієм у своїй базі даних. Код сценарію нижче:

IF Exists(Select * From sys.sysobjects A Where A.name =N'fn_myfunc' and xtype=N'FN') return;

CREATE FUNCTION fn_myfunc ()
returns varchar(10)
AS Begin
...
End

Але коли я виконую описаний вище сценарій, SQL Server повертає помилку:

'CREATE FUNCTION' must be the first statement in a query batch.

Відповіді:


16

Оновлення січня 2017 року - SQL Server 2016+ / база даних SQL Azure

SQL Server 2016 та поточна версія бази даних Azure SQL тепер мають такий синтаксис для функцій, процедур, таблиць, баз даних тощо ( DROP IF EXISTS):

DROP FUNCTION IF EXISTS dbo.fn_myfunc;

А пакет оновлень 1 з пакетом оновлень SQL Server 2016 додає ще кращі функціональні можливості для модулів (функції, процедури, тригери, види), що означає відсутність втрати дозволів або залежностей ( CREATE OR ALTER):

CREATE OR ALTER FUNCTION dbo.fn_myfunc ...

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

Але, якщо ви використовуєте ...


Старіші версії

Вам потрібно зробити те, що робить SQL Server, коли ви скриптуєте це з програми Management Studio:

IF NOT EXISTS (SELECT 1 FROM sys.objects WHERE type = 'FN' AND name = 'fn_myfunc')
BEGIN
    DECLARE @sql NVARCHAR(MAX);
    SET @sql = N'CREATE FUNCTION ...';
    EXEC sp_executesql @sql;
END

Або ви можете сказати:

BEGIN TRY
    DROP FUNCTION dbo.fn_myfunc;
END TRY
BEGIN CATCH
    PRINT 'Function did not exist.';
END CATCH
GO
CREATE FUNCTION...

Або ви можете просто сказати:

DROP FUNCTION dbo.fn_myfunc;
GO
CREATE FUNCTION...

(Тут ви отримаєте повідомлення про помилку, якщо функція ще не існує, але сценарій продовжуватиметься від наступного GO, тож, крапля працювала чи ні, функція все одно буде (знову) створена.)

Зауважте, що якщо ви кинете функцію та відновите її знову, ви також втратите дозволи та інформацію про залежність.


1
значить, немає ключового слова $$ СТВОРИТИ АБО ЗАМОВИТИ $$? жалість.
цинкування

@zinking ні, але, можливо, у майбутній версії. Будь ласка, голосуйте / коментуйте: connect.microsoft.com/SQLServer/feedback/details/127219/…
Аарон Бертран

1
@AaronBertrand Хороша ідея проголосувати за неї, але посилання порушено
синюватий

@bluish Так вибачте, вони видалили елемент десь між трьома роками тому, коли я опублікував посилання, і зараз ... Спробуйте connect.microsoft.com/SQLServer/feedback/details/344991/… або connect.microsoft.com/SQLServer/feedback / деталі / 351217 /…
Аарон Бертран

Це може бути доступне зараз: blogs.msdn.microsoft.com/sqlserverstorageengine/2016/11/17/…
Бруно

1

Помилка досить зрозуміла. Є кілька способів її виправити.

  1. Розділіть сценарій на різні партії в студії управління за допомогою GOпсевдо-ключового слова та DROP/ CREATEоб'єкта. (Зверніть увагу, що саме ключове слово можна змінити в параметрах Studio Studio, але це фактичне налаштування, тому я пропоную залишити його в спокої).

    Коли ви запускаєте скрипт (або вибрану частину сценарію), Management Studio відокремлює кожен фрагмент сценарію між GOs та послідовно надсилає частини на SQL Server окремими партіями.

  2. Використовуйте динамічний SQL для надсилання окремої партії з іншої партії.

    Це кращий метод, тому що тоді ваш скрипт не залежить від зовнішньої функціональності для правильного виконання. Наприклад, якщо у вашій програмі є програма оновлення бази даних, загалом кажучи, вона завантажить файл сценарію та виконає його на цільовому сервері. Або вам доведеться додати логіку, щоб розділити партії, як це робить студія Management (зауважте: загрожує небезпекою), або написати сценарій так, що весь сценарій може бути успішно виконаний у вигляді однієї партії.

    Як було сказано в іншій відповіді, ви можете зробити тест / CREATEза допомогою цього методу (або якоїсь іншої комбінації DROP/ CREATEтощо). Що я вважаю за краще зробити, це створити об'єкт заглушки, якщо об'єкта не існує, а потім використовувати, ALTER <object>щоб насправді створити або змінити. Цей підхід не скасовує залежності, такі як дозволи або розширені властивості, і не потрібно копіювати / вставляти схильну до помилок логіку, щоб робити CREATE/ ALTERв одному операторі.

    Ось шаблон, який я використовую для створення або зміни скалярної функції. Я залишу це як вправу для читача, щоб адаптувати це до інших типів об’єктів (зберігаються програми, тригери тощо).

IF NOT EXISTS(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[<schema>].[<function name>]') AND type IN ('FN', 'FS'))
    EXEC sp_executesql N'CREATE FUNCTION [<schema name>].[<function name>] (@a int) RETURNS int AS BEGIN /* Stub */ RETURN @a END'

EXEC sp_executesql N'
ALTER FUNCTION [<schema name>].[<function name>]
/* ... */
'

Як тут може працювати варіант 1? Додавання GOфактично гарантує, що скрипт буде зламаний, оскільки IFне призведе нікуди.
Аарон Бертран

@Aaron: Я думав про DROP/ CREATEсценарій - відредаговано. Спасибі.
Джон Сейгель

1

У вас є можливість перевірити, чи існує об'єкт в databaseі створити, якщо ні:

IF OBJECT_ID('new_function', 'FN') IS NULL
BEGIN
  EXEC('CREATE FUNCTION new_function() RETURNS INT AS BEGIN RETURN 1 END');
END;
go

ALTER FUNCTION new_function() RETURNS INT AS
BEGIN

...
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.