有啥方法可以使这个 UDF 具有确定性吗?

Posted

技术标签:

【中文标题】有啥方法可以使这个 UDF 具有确定性吗?【英文标题】:Is there any way to make this UDF deterministic?有什么方法可以使这个 UDF 具有确定性吗? 【发布时间】:2010-01-22 16:26:23 【问题描述】:

我认为这不是确定性的,仅仅是因为DB_NAME() 不是确定性的?如果DB_NAME() 不是确定性的,为什么它不是确定性的?

ALTER FUNCTION [TheSchema].[udf_IS_PRODUCTION] ()
RETURNS bit
    WITH SCHEMABINDING
AS 
    BEGIN
        RETURN CASE WHEN DB_NAME() = 'PRODUCTION' THEN CONVERT(bit, 1) ELSE CONVERT(bit, 0) END
    END

更新:此版本有效,具有确定性,允许在任何数据库中使用相同的代码并删除数据库名称的硬编码(这也允许我删除另一个自动系统健康异常数据库名称编码)

ALTER FUNCTION [TheSchema].[udf_IS_PRODUCTION] ()
RETURNS bit
    WITH SCHEMABINDING
AS 
    BEGIN
        RETURN (SELECT IS_PRODUCTION FROM TheSchema.IS_PRODUCTION)
    END

仅供参考这是我的系统健康自我报告系统中的代码 sn-p,我用它来监控潜在问题。

    SELECT  'Non-deterministic Scalar UDF' AS Problem
           ,QUOTENAME(ROUTINE_SCHEMA) + '.' + QUOTENAME(ROUTINE_NAME) AS ROUTINE_NAME
    FROM    INFORMATION_SCHEMA.ROUTINES WITH (NOLOCK)
    WHERE   IS_DETERMINISTIC = 'NO'
            AND ROUTINE_TYPE = 'FUNCTION'
            AND DATA_TYPE <> 'TABLE'
    ORDER BY ROUTINE_SCHEMA
           ,ROUTINE_NAME

【问题讨论】:

因为重命名数据库不会锁定里面的所有数据?基本上有人可以在长时间运行的查询期间重命名数据库。 【参考方案1】:

当然,我可以想出一种方法来使其具有确定性。在您的生产数据库上部署此函数:

ALTER FUNCTION [TheSchema].[udf_IS_PRODUCTION] ()
RETURNS bit
    WITH SCHEMABINDING
AS 
BEGIN
    RETURN CONVERT(bit, 1)
END

并将这个部署到您的测试数据库中:

ALTER FUNCTION [TheSchema].[udf_IS_PRODUCTION] ()
RETURNS bit
    WITH SCHEMABINDING
AS 
BEGIN
    RETURN CONVERT(bit, 0)
END

这可能看起来很愚蠢,但 IMO 数据库名称不应该比某些 UDF 的返回值更“硬编码”。

更好的是,只需将此信息放在某个配置表中即可。

【讨论】:

出于显而易见的原因,我希望数据库之间的代码相同。此外,我更愿意封装调用以使系统更易于维护、清晰和可读。使用配置表的版本具有确定性。谢谢。 它还允许清除关于硬编码数据库名称的另一个健康异常。超级。【参考方案2】:

难道你不能重写你的函数而不是在内部确定 DB_NAME(),而是让它作为参数发送吗??

ALTER FUNCTION [TheSchema].[udf_IS_PRODUCTION] (DatabaseName VARCHAR(255))
RETURNS bit
WITH SCHEMABINDING
AS 
    BEGIN
        RETURN CASE WHEN DatabaseName = 'PRODUCTION' 
                    THEN CONVERT(bit, 1) 
                    ELSE CONVERT(bit, 0) 
               END
    END

不应该是确定性的,对吧?

调用的时候可以使用DB_NAME()作为函数来判断数据库名

【讨论】:

我不希望调用者做这项工作,这可能会鼓励他们简单地内联逻辑,从而违背封装的目的。我发布了我的最终解决方案。【参考方案3】:

根据定义,确定性函数是一个函数,其返回值由其参数的值唯一标识。

现在,给定DB_NAME() 的参数(没有参数),你能知道它会返回什么吗?

【讨论】:

这是严格确定性的定义,所有内部函数都必须严格确定才能被标记为确定性,然后用于制作严格确定性或确定性的用户定义函数。【参考方案4】:

严格意义上的确定性,结果不是基于输入参数,而是基于不受您控制的外部对象的状态。

名称可以更改等,

Alter Database Modify Name = new_name

在 2005 年,当我尝试使用默认架构时,SQL 不会阻止创建该函数。 如果您遇到一种情况,它拒绝接受基于非确定性的函数并且您必须解决它(存在风险等),围绕它的路线是创建一个使用该函数的视图,然后从函数内的视图。

【讨论】:

函数有效且工作正常,只是不确定。我有一个自动系统健康报告,它标记所有非确定性标量函数以供审查。显然,DB_NAME 不是严格确定性的,对于所有内置函数,严格确定性和确定性的处理方式相同——只有严格确定性的函数才被标记为确定性。依赖于数据库状态的 UDF 仍然是确定性的,只是不是严格确定性的。 您的报告看起来非常精确,具体到严格的确定性,从这个意义上说,它是正确的。 我会将我的健康报告代码添加到答案中 - UDF 只有一个确定性标志。 请对你的答案做一个小改动,以便我给你投票,我一定是点击了太多次。

以上是关于有啥方法可以使这个 UDF 具有确定性吗?的主要内容,如果未能解决你的问题,请参考以下文章

确定货币或双倍的 UDF 方法

为啥 SQL 2005 说这个 UDF 是不确定的?

Legacy UDF - 确定字典/地图中的最大值

MSSQL 为啥这个函数是不确定的

网页导出无法生成excel,有啥方法可以解决吗?

有啥方法可以获取 firebase 请求的状态吗?