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 - 不,只要ARITHABORT
或ANSI_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 确定性用户定义函数的主要内容,如果未能解决你的问题,请参考以下文章