修飾されていないインデックス付きビューでのクラスター化インデックス挿入演算子の防止


8

誰かがこれの回避策を知っていますか?本質的に、ストアドプロシージャは、行が修飾されていなくても、インデックス付きビューに対して挿入演算子を強制します。その結果、キャストエラーが発生します。ただし、アドホックの場合、sqlはビューを考慮から正しく除外します。

次のスキーマを検討してください。

create table testdata (
    testid int identity(1,1) primary key
  , kind varchar(50)
  , data nvarchar(4000))
go
create view integer_testdata with schemabinding
as
select cast(a.data as int) data, a.kind, a.testid
  from dbo.testdata a
 where a.kind = 'integer'
go
create unique clustered index cl_intdata on integer_testdata(data)
go
create procedure insert_testdata
(
    @kind varchar(50)
  , @data nvarchar(4000)
)
as
begin
  insert into testdata (kind, data) values (@kind, @data)
end
go

これらはすべて機能します:

insert into testdata (kind, data) values ('integer', '1234');
insert into testdata (kind, data) values ('integer', 12345);
insert into testdata (kind, data) values ('noninteger', 'noninteger');
exec insert_testdata @kind = 'integer', @data = '123456';
exec insert_testdata @kind = 'integer', @data = 1234567;

これは失敗します:

exec insert_testdata @kind = 'noninteger', @data = 'noninteger';

「推定実行計画」の比較:

insert into testdata (kind, data) values ('noninteger', 'noninteger')ここに画像の説明を入力してください

exec insert_testdata @kind = 'noninteger', @data = 'noninteger'ここに画像の説明を入力してください


アドホックとキャッシュされたストアドプロシージャプランの顕著な違いは、偶然ですか?
Ali Razeghi、2015年

はい、アドホックを実行すると、インデックス付きビューに対して演算子が得られません... SQLはビューにフィルターがあることを確認し、それを考慮から除外するのに十分スマートだと思います(この除外は、 proc)
cocogorilla

4
テストする立場にないが、追加のoption (recompile)助けはありますか?
マーティンスミス

2
好奇心から、あなたはどんな問題を解決しようとしていますか?これはXY問題のようなにおいがします
Max Vernon、

1
@MaxVernon既存のデータ構造を使用していて、nvarchar(4000)のサブセットに格納されている一意の整数値をすばやく検索する必要があります。別の列のフィルターが行のサブセットを定義します。
ココゴリラ2015年

回答:


6

問題を再現するための完全なスクリプトを提供していただきありがとうございます。

SQL Server 2014 Expressでテストしました。

追加するOPTION(RECOMPILE)と機能します:

ALTER procedure [dbo].[insert_testdata]
(
    @kind varchar(50)
  , @data nvarchar(4000)
)
as
begin
  insert into testdata (kind, data) 
  values (@kind, @data)
  OPTION(RECOMPILE);
end

これをSSMSで実行すると:

exec insert_testdata @kind = 'noninteger', @data = 'noninteger';

私はこのメッセージを受け取ります:

(1 row(s) affected)

そして、行がテーブルに追加されます。

SQL Serverのどのバージョンを使用していますか?2008年以前のバージョンでは、これOPTION(RECOMPILE)が少し異なる動作をしたことを漠然と覚えています。


私は既存のデータ構造を使用していて、nvarchar(4000)のサブセットに格納されている一意の整数値を高速に検索する必要があります。別の列のフィルターが行のサブセットを定義します。

この場合、インデックス付きビューの代わりにフィルターされたインデックスを使用する方がよい場合があります。

CREATE UNIQUE NONCLUSTERED INDEX [IX_DataFiltered] ON [dbo].[testdata]
(
    [data] ASC
)
WHERE ([kind]='integer')

WHEREクエリのフィルターがWHEREインデックスの句と完全に一致する場合、オプティマイザーはこのインデックスを使用する必要があります。

はい、ここでのインデックスはnvarchar列にあります。このテーブルをint別のテーブルの列と結合する場合、または値を使用してこの列の値をフィルタリングしようとする場合は特に、これは最善ではない可能性がありintます。


頭に浮かぶもう一つの変異体である計算列を持続変換するnvarcharint。本質的にはビューとよく似ていますが、nvarchar変換された永続化された値intは、別のオブジェクトではなく同じテーブルに格納されます。

CREATE TABLE [dbo].[testdata](
    [testid] [int] IDENTITY(1,1) NOT NULL,
    [kind] [varchar](50) NULL,
    [data] [nvarchar](4000) NULL,
    [int_data]  AS (case when [kind]='integer' then CONVERT([int],[data]) end) PERSISTED,
PRIMARY KEY CLUSTERED 
(
    [testid] ASC
))


CREATE UNIQUE NONCLUSTERED INDEX [IX_int_data_filtered] ON [dbo].[testdata]
(
    [int_data] ASC
)
WHERE ([kind]='integer')

この設定で、元のストアドプロシージャを使用して行を挿入しようとしましたが、それがなくても機能しましたOPTION(RECOMPILE)


実際、上記の永続化された列が機能する主な理由は、私がを使用しているためCASEです。CASEビューの定義に追加すると、ストアドプロシージャはなしでも機能しOPTION(RECOMPILE)ます。

create view integer_testdata2 with schemabinding
as
select 
    case when a.kind='integer' then CONVERT(int, a.data) end as data
    , a.kind, a.testid
from dbo.testdata a
where a.kind = 'integer'
go

列幅が4000(900の制限をはるかに超える)であるため、フィルター処理されたインデックスが適切に機能するかどうかはわかりません。クエリヒントオプションの再コンパイルを使用することは考えていませんでした...プロシージャ全体に再コンパイルを適用していました。あなたの提案は私のすべてのテストケースで機能します!ありがとうございました。
ココゴリラ

1
はい、元の列のフィルター処理されたインデックスはあまり役に立ちません。計算列が永続化された別のバリアントを追加しました。
Vladimir Baranov

私は永続的な計算列オプションが大好きです...それは正しい解決策として私を襲います
cocogorilla '28 / 10/28
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.