通过代码创建新函数(如果不存在)


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.

Answers:


16

2017年1月更新-SQL Server 2016 + / Azure SQL数据库

SQL Server 2016和当前版本的Azure SQL数据库现在对函数,过程,表,数据库等具有以下语法(DROP IF EXISTS):

DROP FUNCTION IF EXISTS dbo.fn_myfunc;

SQL Server 2016 Service Pack 1为模块(功能,过程,触发器,视图)添加了更好的功能,这意味着不会丢失权限或依赖项(CREATE OR ALTER):

CREATE OR ALTER FUNCTION dbo.fn_myfunc ...

这两种语法增强功能都可以简化用于源代码控制,部署等的脚本。

但是,如果您正在使用...


旧版本

从Management Studio编写脚本时,您需要执行SQL Server的操作:

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
因此没有关键字$$ CREATE OR REPLACE $$吗?可怜。
zinking 2012年

@zinking不,但可能在将来的版本中。请投票/评论:connect.microsoft.com/SQLServer/feedback/details/127219/…–
Aaron Bertrand

1
@AaronBertrand投票的好主意,但链接已断开
蓝蓝的时间为

@bluish是的,对不起,他们在三年前之间发布了链接,现在将其删除了。现在...尝试connect.microsoft.com/SQLServer/feedback/details/344991/…connect.microsoft.com/SQLServer/feedback / details / 351217 /…
亚伦·伯特兰


1

该错误是不言自明的。有几种修复方法。

  1. 使用GO伪关键字和DROP/ CREATE将脚本在Management Studio中分成不同的批次。(请注意,关键字本身可以在Management Studio选项中进行更改,但这实际上是设置,因此建议您不要使用它)。

    运行脚本(或脚本的选定部分)时,Management Studio会在GOs 之间分隔脚本的每个块,然后将这些部分作为单独的批处理顺序发送到SQL Server。

  2. 使用动态SQL从另一个批次中发送单独的批次。

    这是首选方法,因为这样您的脚本将不依赖于外部功能来正确执行。例如,如果您的应用程序具有数据库更新程序,则通常来说它将加载脚本文件,然后在目标服务器上执行它。您将必须像Management Studio一样添加逻辑来分隔批处理(注意:充满危险),或者编写脚本以使整个脚本可以作为一个批处理成功执行。

    如另一个答案中所述,您可以CREATE使用此方法(或DROP/的其他组合CREATE)进行测试/ 。我更喜欢做的是创建一个存根对象(如果该对象不存在),然后用于ALTER <object>实际创建或更改。这种方法不会删除诸如权限或扩展属性之类的依赖关系,也不需要复制/粘贴容易出错的逻辑以在单个语句中执行CREATE/ ALTER

    这是我用于创建或更改标量函数的模板。我将其保留为读者练习,以使其适应其他类型的对象(存储的proc,触发器等)。

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

...
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.