39

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

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

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

4

2 に答える 2

57

作成するだけですwith schemabinding

次に、SQL Server は、決定論的と見なされる基準を満たしているかどうかを確認します (これは、外部テーブルにアクセスしたり、 などの非決定論的関数を使用したりしないためですgetdate())。

あなたはそれが動作したことを確認することができます

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

元のコードに schemabinding オプションを追加すると問題なく動作しますが、もう少し単純なバージョンでも問題ありません。

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
于 2010-09-06T13:07:46.160 に答える
10

WITH SCHEMABINDING計算列のインデックスの「決定論的」要件を緩和するには、ユーザー定義関数を宣言する必要があります。

宣言WITH SCHEMABINDINGされた関数は、関数で使用されるオブジェクトの依存関係 (テーブル内の列など) に関する追加の知識を保持し、関数自体が事前に削除されない限り、これらの列への変更を防ぎます。

決定論的関数は、Sql Server がその実行計画を最適化するのにも役立ちます。最も顕著なのはハロウィーン保護の問題です。

スキーマ バインド関数を使用して計算列にインデックスを作成する例を次に示します。

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
于 2010-09-06T13:14:25.597 に答える