如果不存在则创建UDF(用户定义函数),如果存在则跳过它

Asy*_*ous 30 sql t-sql sql-server

嗨,谢谢你读这篇文章.

我试图使用IF EXISTS/IF NOT EXISTS语句来检查对象是否存在.基本上我想跳过它,如果它在那里或创建它,如果它不存在.

我用两种不同的方式编写代码,但是我收到一个错误:创建函数必须是批处理中唯一的函数.如果我把GO放在下面的插图语句之间,我会收到另一个警告:GO附近的语法不正确.

我在哪里错了?

IF NOT EXISTS
(select * from Information_schema.Routines where SPECIFIC_SCHEMA='dbo' 
AND SPECIFIC_NAME = 'FMT_PHONE_NBR' AND Routine_Type='FUNCTION')

/*CREATE FUNCTION TO FORMAT PHONE NUMBERS*/
CREATE FUNCTION [dbo].[FMT_PHONE_NBR](@phoneNumber VARCHAR(12))
RETURNS VARCHAR(12)
AS
BEGIN
    RETURN SUBSTRING(@phoneNumber, 1, 3) + '-' + 
           SUBSTRING(@phoneNumber, 4, 3) + '-' + 
           SUBSTRING(@phoneNumber, 7, 4)
END

GO
Run Code Online (Sandbox Code Playgroud)

或这个:

IF NOT EXISTS
(SELECT name FROM sys.objects WHERE name = 'dbo.FMT_PHONE_NBR')

GO

/*CREATE FUNCTION TO FORMAT PHONE NUMBERS*/
CREATE FUNCTION [dbo].[FMT_PHONE_NBR](@phoneNumber VARCHAR(12))
RETURNS VARCHAR(12)
AS
BEGIN
    RETURN SUBSTRING(@phoneNumber, 1, 3) + '-' + 
           SUBSTRING(@phoneNumber, 4, 3) + '-' + 
           SUBSTRING(@phoneNumber, 7, 4)
END

GO
Run Code Online (Sandbox Code Playgroud)

感谢您查看此内容!

Tre*_*eyE 40

解决此问题的最简单方法是删除已存在的函数,然后重新创建它:

/* If we already exist, get rid of us, and fix our spelling */
IF OBJECT_ID('dbo.FMT_PHONE_NBR') IS NOT NULL
  DROP FUNCTION FMT_PHONE_NBR
GO

/*CREATE FUNCTION TO FORMAT PHONE NUMBERS*/
CREATE FUNCTION [dbo].[FMT_PHONE_NBR](@phoneNumber VARCHAR(12))
RETURNS VARCHAR(12)
AS
BEGIN
    RETURN SUBSTRING(@phoneNumber, 1, 3) + '-' + 
           SUBSTRING(@phoneNumber, 4, 3) + '-' + 
           SUBSTRING(@phoneNumber, 7, 4)
END

GO
Run Code Online (Sandbox Code Playgroud)

请注意上面"object_id"函数的用法.这实际上是一种检查对象是否存在的常用方法,尽管它受到某些约束.

你可以在这里阅读更多相关信息:OBJECT_ID

  • 公平的警告.此特定方法将导致任何显式权限丢失.如果通过在架构或数据库级别授予执行权限来授予权限,则可以.但是,如果您将其授予FMT_PHONE_NBR,例如,在运行此脚本后,您的权限将会消失. (2认同)

Phi*_*ley 24

当我长时间在这堵砖墙上打我的头时,我会再扔两美分.

正如所指出的那样,只有在它不存在的情况下添加它才会很好,但是如果不使用动态SQL就不可能在T-SQL中添加...并且包装你的函数,过程,触发器,视图,甚至可能是更加模糊不清的物体,因为动态陈述太不切实际了.(不要让我支持连续可能包含4个以上单个撇号的源代码!)

删除(如果存在)和(重新)创建是可行的解决方案.据推测,如果您要推出新代码,您可能希望创建该对象(如果该对象尚未存在),否则将删除现有/旧代码并将其替换为新代码.(如果您可能不小心将"新"代码替换为"旧"代码,则会出现版本控制问题,这是一个不同且难度较大的主题.)

丢弃旧代码时,真正的问题是丢失信息.什么信息?我经常遇到的是访问权限:谁拥有EXECUTE或者对某些功能SELECT拥有对象权限?丢弃和更换,他们走了.当然,答案是将访问权限编写为部署脚本的一部分.但是,如果您遇到不同数据库托管环境具有不同配置(登录,域,组等)的情况,您可能会遇到以下情况:无法知道现有访问权限是什么在给定的实例上,如果您只是删除并重新创建它,现有用户可能无法再访问它.(扩展属性和esoterica的其他位同样会受到影响.)

对此的第一个也是最好的解决方案是实现强大的安全性.设置数据库角色,为角色分配/关联适当的权限,然后您不必知道角色中的谁 - 这将是环境管理员的工作.(你仍然需要GRANT EXECUTE on ThisProc to dbo.xxx在脚本的末尾有类似的东西,但这并不是那么难.

如果像我一样,你(a)没有被授权推出一个好的和强大的安全模型,并且(b)是懒惰的,并且可能不会检查数百行长存储过程文件的结尾访问权限代码,您可以执行以下操作.(这是为存储过程设置的,但适用于函数和其他对象.)

-- isProcedure
-- IsScalarFunction    (Returns single value)
-- IsTableFunction     (Declared return table structure, multiple statements)
-- IsInlineFunction    (Based on single select statement)
-- IsView

IF objectproperty(object_id('dbo.xxx'), 'isProcedure') is null
 BEGIN
    --  Procedure (or function) does not exist, create a dummy placeholder
    DECLARE @Placeholder varchar(100)
    SET @Placeholder = 'CREATE PROCEDURE dbo.xxx AS RETURN 0'
    EXEC(@PlaceHolder)

    --  Configure access rights
    GRANT EXECUTE on dbo.xxx TO StoredProcedureUser
 END
GO

ALTER PROCEDURE dbo.xxx
(etc.)
GO
Run Code Online (Sandbox Code Playgroud)

这将:

  • 首先检查程序是否存在.如果没有,请创建"placholder",并为其设置适当的访问权限
  • 然后,在脚本运行之前是否存在,ALTER并使用所需的代码进行设置.

还存在在模式中管理基于代码的对象(主要是存储过程)的问题,其中模式可能不存在.我还没有想出那一个,如果你很幸运,你将永远不会陷入同样的​​怪异局面.


Ste*_*ger 8

死灵法术。
删除不是一个好主意,因为可能在对象上设置了权限。

因此,正确的做法实际上是
A) 如果该函数不存在(虚拟),则创建该函数
B) 如果该函数已经存在,则更改该函数。(它可能不是最新的)

例子:

-- DROP FUNCTION IF EXISTS [dbo].[TestFunction]

-- Do not drop the function if it exists - there might be privileges granted on it... 
-- You cannot alter function from table-valued function to scalar function or vice-versa 
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestFunction]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) 
BEGIN
    -- CREATE FUNCTION dbo.[TestFunction]() RETURNS int AS BEGIN RETURN 123 END 
    -- CREATE FUNCTION dbo.[TestFunction]() RETURNS table AS RETURN (SELECT * FROM information_schema.tables)  
    EXECUTE('
        CREATE FUNCTION dbo.[TestFunction]() RETURNS int AS BEGIN RETURN 123 END 
    ')
END 
GO



-- ALTER FUNCTION dbo.[TestFunction](@abc int) RETURNS table AS RETURN (SELECT * FROM information_schema.tables)  
ALTER FUNCTION dbo.[TestFunction]() RETURNS int AS BEGIN RETURN 'test' END 
Run Code Online (Sandbox Code Playgroud)

请注意,您不能将表值函数更改为标量函数,反之亦然。
但是,您可以随意更改参数类型和参数数量 - 以及返回模式。