Address
という永続的な計算列があるHashkey
というテーブルがあります。列は確定的ですが正確ではありません。シークできない一意のインデックスがあります。このクエリを実行すると、主キーが返されます。
SELECT @ADDRESSID= ISNULL(AddressId,0)
FROM dbo.[Address]
WHERE HashKey = @HashKey
私はこの計画を手に入れました:
インデックスを強制すると、さらに悪い計画になります:
インデックスとシークの両方を強制しようとすると、エラーが発生します。
このクエリでヒントが定義されているため、クエリプロセッサはクエリプランを作成できませんでした。ヒントを指定せず、
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
この問題は、[TestGeocode].[ToString]()
がmax
データ型(nvarchar(max)
)を返すという事実に関連しているようです。
この単純なバージョンでも問題が発生します(_c1
_の定義をvarchar(8000)
に変更するか、COALESCE
の代わりにISNULL
を使用すると解決します)
_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)
)のデータ型を返します。ここでもCOALESCE
の戻り値の型はVARCHAR(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
_
Ispeculate失敗した場合、(暗黙の)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])
)
_
@HashKey
のデータ型がインデックス付きの列のデータ型と一致しない場合、引数を使用できない式が原因でこれらの症状が発生します。必要なデータ型を強制するために、計算列式で明示的なCAST
が必要になる場合があります。
あなたの再現に基づいて、これはバグだと思います。 Martinの回避策のバージョンも含めて、Connectのバグ Computed Column Index Not Used を提出しました。自由に投票してください。