Sql Server 确定性用户定义函数

Posted

技术标签:

【中文标题】Sql Server 确定性用户定义函数【英文标题】:Sql Server deterministic user-defined function 【发布时间】:2019-06-10 09:46:45 【问题描述】:

我有如下用户自定义函数:

create function [dbo].[FullNameLastFirst]
(
    @IsPerson bit,
    @LastName nvarchar(100),
    @FirstName nvarchar(100)
)
returns nvarchar(201)
as
begin
    declare @Result nvarchar(201)
    set @Result = (case when @IsPerson = 0 then @LastName else case when @FirstName = '' then @LastName else (@LastName + ' ' + @FirstName) end end)
    return @Result
end

我无法使用此函数在计算列上创建索引,因为它不是确定性的。 有人可以解释为什么它不是确定性的,最终如何修改以使其具有确定性? 谢谢

【问题讨论】:

【参考方案1】:

您只需要创建它with schemabinding

然后,SQL Server 将验证它是否满足被视为确定性的标准(它这样做是因为它不访问任何外部表或使用非确定性函数,例如 getdate())。

您可以验证它是否适用于

SELECT OBJECTPROPERTY(OBJECT_ID('[dbo].[FullNameLastFirst]'), 'IsDeterministic')

将架构绑定选项添加到您的原始代码中可以正常工作,但版本会稍微简单一些。

CREATE FUNCTION [dbo].[FullNameLastFirst] (@IsPerson  BIT,
                                           @LastName  NVARCHAR(100),
                                           @FirstName NVARCHAR(100))
RETURNS NVARCHAR(201)
WITH SCHEMABINDING
AS
  BEGIN
      RETURN CASE
               WHEN @IsPerson = 0
                     OR @FirstName = '' THEN @LastName
               ELSE @LastName + ' ' + @FirstName
             END
  END

【讨论】:

我创建了指向计算列的索引。我还有一个引用我的表的视图。我想我也必须在视图上指定 SchemaBinding 才能在同一列上创建索引。关于这一点,如果我的基表作为计算列上的索引,那么在视图上创建另一个索引是多余的吗? @opaera - 是的 - 您也不需要在视图中索引该列。 最后一个问题,如果可以的话。我有一个对视图(引用我的索引表的视图)进行查询的 sp。我可以/应该在查询子句中指定索引名称吗?例如Select * From MyView (With MyTableIndex) ... 也许我这样做有一些好处? @opaera - 不,只要ARITHABORTANSI_WARNINGS 处于打开状态,它就应该自动使用持久列值。您可以检查执行计划来确定这一点。【参考方案2】:

您需要声明用户定义函数WITH SCHEMABINDING 来满足计算列上索引的“确定性”要求。

声明为WITH SCHEMABINDING 的函数将保留有关函数中使用的对象依赖关系的额外信息(例如表中的列),并将防止对这些列进行任何更改,除非事先删除了函数本身。

确定性函数还可以帮助 Sql Server 优化其执行计划,尤其是Halloween Protection 问题。

以下是使用模式绑定函数在计算列上创建索引的示例:

create function [dbo].[FullNameLastFirst] 
( 
    @IsPerson bit, 
    @LastName nvarchar(100), 
    @FirstName nvarchar(100) 
) 
returns nvarchar(201) 
with schemabinding
as 
begin 
    declare @Result nvarchar(201) 
    set @Result = (case when @IsPerson = 0 then @LastName 
                        else case when @FirstName = '' then @LastName 
                                  else (@LastName + ' ' + @FirstName) end end) 
    return @Result 
end 


create table Person
(
  isperson bit,
  lastname nvarchar(100),
  firstname nvarchar(100),
  fullname as [dbo].[FullNameLastFirst] (isperson, lastname, firstname)
)
go
insert into person(isperson, lastname, firstname) values (1,'Firstname', 'Surname')
go

create index ix1_person on person(fullname)
go

select fullname from Person with (index=ix1_person) where fullname = 'Firstname Surname'
go

【讨论】:

以上是关于Sql Server 确定性用户定义函数的主要内容,如果未能解决你的问题,请参考以下文章

缓存用户定义函数的输出(ms sql server)

SQL Server 自定义函数

SQL Server 自定义函数

SQL Server用户定义的函数(UDF)使用详解

SQL Server 中对用户定义函数的需求是啥?

数据库原理与应用(SQL Server)笔记 第十章 用户定义函数