web-dev-qa-db-ja.com

SQLServerの決定論的なユーザー定義関数

次のユーザー定義関数があります。

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

この関数を使用して計算列にインデックスを作成することはできません。これは決定論的ではないためです。なぜそれが決定論的ではないのか、そして最終的にはそれを決定論的にするためにどのように修正するのかを誰かが説明できますか?ありがとう

35
opaera

_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
_
51
Martin Smith

ユーザー定義関数を宣言する必要があります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
8
StuartLC