シークできない永続化計算列のインデックス


15

という名前のテーブルがありAddress、そのテーブルには、という永続的な計算列がありHashkeyます。列は確定的ですが、正確ではありません。シークできない一意のインデックスがあります。このクエリを実行すると、主キーが返されます。

SELECT @ADDRESSID= ISNULL(AddressId,0)
FROM dbo.[Address]
WHERE HashKey = @HashKey

私はこの計画を取得します:

基本計画

インデックスを強制すると、さらに悪い計画が得られます。

ForceIndex

インデックスとシークの両方を強制しようとすると、エラーが発生します。

このクエリで定義されたヒントのため、クエリプロセッサはクエリプランを作成できませんでした。ヒントを指定せずに、使用せずにクエリを再送信しますSET FORCEPLAN

これは、正確ではないという理由だけですか?持続するかどうかは関係ないと思いましたか?

これを非計算列にすることなく、このインデックスをシーク可能にする方法はありますか?

これに関する情報へのリンクはありますか?

実際のテーブル作成を投稿することはできませんが、同じ問題があるテストテーブルを次に示します。

drop TABLE [dbo].[Test]

CREATE TABLE [dbo].[Test]
  (
     [test]        [VARCHAR](100) NULL,
     [TestGeocode] [geography] NULL,
     [Hashkey] AS CAST(
                        ( hashbytes
                            ('SHA', 
                                ( RIGHT(REPLICATE(' ', (100)) + isnull([test], ''), ( 100 )) ) 
                                + RIGHT(REPLICATE(' ', (100)) + isnull([TestGeocode].[ToString](), ''), ( 100 ))
                            ) 
                        ) AS BINARY(20)                                                                                                        
                      ) PERSISTED
    CONSTRAINT [UK_Test_HashKey] UNIQUE NONCLUSTERED([Hashkey])
  )    
GO    
DECLARE @Hashkey BINARY(20)

SELECT [Hashkey]
FROM   [dbo].[Test] WITH (FORCESEEK) /*Query processor could not produce a query plan*/
WHERE  [Hashkey] = @Hashkey 

回答:


12

この問題[TestGeocode].[ToString]()は、maxデータ型(nvarchar(max))を返すという事実に関連しているようです。

また、この単純なバージョンで問題が発生します(定義を変更c1するvarchar(8000)COALESCEISNULL解決する代わりに使用する)

DROP TABLE dbo.Test

CREATE TABLE dbo.Test
  (
     c1        VARCHAR(
                          MAX    --Fails
                        --  8000 --Works fine
                          ) NULL,
     comp1 AS CAST(ISNULL(c1, 'ABC') AS VARCHAR(100))
    CONSTRAINT UK_Test_comp1 UNIQUE NONCLUSTERED(comp1)
  )

GO

DECLARE @comp1 VARCHAR(100)

SELECT comp1
FROM   dbo.Test WITH (FORCESEEK)
WHERE  comp1 = @comp1 
OPTION (QUERYTRACEON 3604, QUERYTRACEON 8606); 

計算列の参照は、基になる定義に展開され、後で列に一致します。これにより、計算列を名前でまったく参照せずに一致させることができ、基礎となる定義で操作を簡素化することもできます。

ISNULL最初のパラメーターのデータ型を返します(VARCHAR(MAX)私の例では)。の戻りタイプCOALESCEVARCHAR(MAX)ここにありますが、問題を回避する方法で異なる方法で評価されるようです。

クエリが成功した場合、トレースフラグの出力には次のものが含まれます。

ScaOp_Convert varchar(max) collate 49160,Null,Var,Trim,ML=65535

    ScaOp_Const TI(varchar collate 49160,Var,Trim,ML=3) 
                      XVAR(varchar,Owned,Value=Len,Data = (3,ABC))

失敗した場合、これは

ScaOp_Identifier COL: ConstExpr1003 

失敗した場合、(暗黙の)は一度だけ実行され、これは実行時定数(詳細情報)として評価されると推測CAST('ABC' AS VARCHAR(MAX))ます。ただし、実際の文字列リテラル値自体ではなく、このランタイム定数ラベルへの参照により、計算列定義との一致が妨げられます。

この書き換えにより、クエリの問題を回避できます

CREATE TABLE [dbo].[Test]
  (
     [test]        [VARCHAR](100) NULL,
     [TestGeocode] [geography] NULL,
     [Hashkey] AS CAST(
                        ( hashbytes
                            ('SHA', 
                                ( RIGHT(SPACE(100) + isnull([test], ''), 100) ) 
                                + RIGHT(SPACE(100) + isnull(CAST(RIGHT([TestGeocode].[ToString](),100) AS VARCHAR(100)), ''),100)
                            ) 
                        ) AS BINARY(20)                                                                                                        
                      ) PERSISTED
    CONSTRAINT [UK_Test_HashKey] UNIQUE NONCLUSTERED([Hashkey])
  )

0

のデータ型が@HashKeyインデックス付き列のデータ型と一致しない場合、非サーガブル式が原因でこれらの症状が発生します。CAST目的のデータ型を強制するために、計算列式に明示的な値が必要になる場合があります。

あなたの再現に基づいて、これはバグだと思います。Martinの回避策のバージョンを使用して、ConnectのバグであるComputed Column Index Not Usedを提出しました。投票してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.