【问题标题】:Can I create create a global function in SQL Server?我可以在 SQL Server 中创建一个全局函数吗?
【发布时间】:2012-05-30 11:20:25
【问题描述】:

是否可以在 SQL Server 中创建一个可以在服务器上的任何数据库中使用的函数,而无需添加数据库前缀?

例如,用这个函数:

CREATE FUNCTION getDays (@date date)
RETURNS INT
AS
BEGIN

RETURN CASE WHEN MONTH(@date) IN (1, 3, 5, 7, 8, 10, 12) THEN 31
            WHEN MONTH(@date) IN (4, 6, 9, 11) THEN 30
            ELSE CASE WHEN (YEAR(@date) % 4    = 0 AND
                            YEAR(@date) % 100 != 0) OR
                           (YEAR(@date) % 400  = 0)
                      THEN 29
                      ELSE 28
                 END
       END

END

【问题讨论】:

    标签: sql sql-server-2008


    【解决方案1】:

    虽然在 master 中创建存储过程使它们全局可用,但这不适用于函数。您需要使用三部分命名约定:

    select <dbname>.<schema>.getDays(...)
    

    为什么微软要让函数不同于存储过程?

    【讨论】:

    • 在 master 中创建函数不会使其可用于其他数据库。此行为适用于存储过程,并且仅当 sp_configure 选项 allow updates 设置为 1 或过程命名为 sp_&lt;something&gt; 时。
    • 我已经在 master 中创建了它,但我的脚本没有看到它
    • 使存储过程在全球范围内可用的功能从未真正供客户使用,它的目的是让诸如 sp_help 之类的系统存储过程存在于一个地方,但能够在任何环境中运行数据库。表、视图、函数等内部从未需要此功能,因此它根本不存在。我不认为官方支持使用此“功能”,并且在将来的某些版本中可能无法使用。
    • Aaron,这是一个很好的答案,但他们为什么不对函数做同样的事情呢?当然,有些系统功能也受益于全球可用。
    • 2000 年没有。整个架构在 2005 年被替换,其中动态管理对象(函数和视图) 全局可用,但不是通过相同的机制。出于向后兼容性的原因,系统过程仍然以这种方式工作,而不是因为它是正确的方式,而且我不希望任何 sp_ 过程(或它们内部的工作方式)在未来发生变化,除了停止 '允许更新或标记系统对象黑客攻击。
    【解决方案2】:

    您可以在master(或其他一些永久数据库)中创建函数,然后在模型数据库中创建同义词:

    USE model;
    GO
    CREATE SYNONYM dbo.getDays FOR master.dbo.getDays;
    

    这将在任何数据库中创建该函数的同义词,但对于现有数据库(或将来附加或恢复的数据库),您需要将同义词复制到那里。这将允许您在任何数据库中引用具有两部分名称的对象,而只需存储一份代码副本。

    顺便说一句,您的代码可以更简洁:

      RETURN (SELECT DATEPART(DAY, DATEADD(DAY, -1, 
         DATEADD(MONTH, 1, DATEADD(DAY, 1-DAY(@date), @date)))));
    

    所以从顶部开始:

    USE [master];
    GO
    DROP FUNCTION dbo.getDays;
    GO
    CREATE FUNCTION dbo.getDays
    (
        @date DATE
    )
    RETURNS INT
    AS
    BEGIN
        RETURN (SELECT DATEPART(DAY, DATEADD(DAY, -1, 
             DATEADD(MONTH, 1, DATEADD(DAY, 1-DAY(@date), @date)))));
    END
    GO
    

    现在在每个数据库中为此创建一个同义词:

    DECLARE @sql NVARCHAR(MAX) = N'';
    
    SELECT @sql += CHAR(13) + CHAR(10) 
    + 'USE ' + QUOTENAME(name) + ';
    
    IF OBJECT_ID(''dbo.getDays'', ''FN'') IS NOT NULL
      DROP FUNCTION dbo.getDays;
    
    IF OBJECT_ID(''dbo.getDays'', ''SN'') IS NOT NULL
      DROP SYNONYM dbo.getDays
    
    CREATE SYNONYM dbo.getDays FOR master.dbo.getDays;'
     FROM sys.databases WHERE name <> 'master';
    
    PRINT @sql;
    
    EXEC sp_executesql @sql;
    

    【讨论】:

    • 'getDays' 不是可识别的内置函数名称。
    • 您需要在master中创建函数,然后在现有数据库中创建同义词(不仅仅是模型)。在模型中创建它只会在模型中创建同义词之后创建的 new 数据库中创建函数。而且你应该总是,总是,总是引用带有模式前缀的函数或同义词,所以它应该是dbo.getDays,而不是getDays
    • 它得到了相同的结果......从内到外工作,从@date 中减去(1-@date 的日期)得到那个月的第一天。加一个月,就是下个月的第一天。然后减去一天。那是@date 月份的最后一天。 DATEPART(DAY) 提取日期(例如,@date = '2005-05-anything' 为 31)...
    • 我已经读到在主数据库中创建自定义函数是不安全的,因为未来的更新可能会删除这个函数。对吗?
    猜你喜欢
    • 2019-05-13
    • 2019-11-26
    • 1970-01-01
    • 2017-10-18
    • 2012-10-29
    • 2019-12-09
    • 1970-01-01
    • 2012-06-18
    • 1970-01-01
    相关资源
    最近更新 更多