web-dev-qa-db-ja.com

ビュー経由で変換されたIDにアクセスするSEEKを取得する方法

テーブルがあると仮定します。

-- just for test purposes
CREATE TABLE SomeTable (
    ID INT IDENTITY(1,1) NOT NULL CONSTRAINT PK__SomeTable__ID PRIMARY KEY CLUSTERED
    ,SomeColumn1 NVARCHAR(50) NULL
    ,SomeColumn2 DATETIME NULL
    );

-- populate table with some rows
INSERT INTO SomeTable DEFAULT VALUES;
GO 1000

サードパーティのアプリケーションがあるため、テーブルのID列をINTからNVARCHARに変換するビューがあります(これは必須であると想定してください)。

CREATE VIEW ThirdPartyView AS
SELECT
    ID = CAST(ID as NVARCHAR(10))
    ,C1 = SomeColumn1
    ,C2 = SomeColumn2
FROM SomeTable;

次に、IDで1つの行にアクセスすると、INDEX SCANを取得します。

SELECT *
FROM ThirdPartyView
WHERE ID = N'1'

enter image description here

理由がわかります。

クエリの外でINDEX SEEKを取得するにはどうすればよいですか?

社説/事情:

  • ベーステーブル(SomeTable)の定義は変更できません(列を追加できません)
  • ビューには同じ列が必要です(列を追加できません)
  • インデックスを定義することは可能です
  • ビューをインデックス付きビューにすることはできますが、そのオプションは避けたい
  • 反対側では、ID列のデータ型がNVARCHARであると想定しています。残念ながらこの条件を満たす必要があります。または私は彼らに会うように言われました。そうでない場合、おそらくアプリケーションは失敗します。
5
jerik1

問題は、ビューを介したクエリが実行と同じであることです

_SELECT *
FROM SomeTable
WHERE CAST(SomeTable.ID as NVARCHAR(10)) = N'1'
_

述語内の列のほとんどすべてのCASTは、その述語を引数なしにレンダリングします。私が認識している唯一の例外は、いくつかの照合順序でのCASTからdatetimeへのdate列とVARCHARからNVARCHARです。

NVARCHARからINTまで、そのような例外はありません。

あなたはそれが何かのようになることを望んでいるかもしれません

_WHERE SomeTable.ID = TRY_CAST('Your search string' as int)
_

しかし、それはそれほど単純ではありません。検索文字列が_'1'_の場合、2つは同じ結果を返しますが、検索文字列_'¹'_(上付き文字1)の場合、intへのキャストは失敗しますが、一部の照合では文字列の比較が等しい。逆に、検索文字列_' 1'_(先行スペースあり)の場合、intキャストと比較は先行スペースを破棄し、等しいと比較しますが、文字列比較は等しくない(およびキャストされる空の文字列と同様) _0_に変換するとint

可能な解決策

一般に、インデックス付きビューまたは計算された列を参照するベーステーブルに新しいインデックスを作成できますが、どちらもビューからCASTを削除するのと比べて非常に最適ではないため、既存のインデックスを検索できます。

計算された列

SomeTableに定義CAST(ID as NVARCHAR(10))を使用して計算列を作成し、それをインデックス付けできます。

_ALTER TABLE SomeTable ADD strID AS CAST(ID as NVARCHAR(10));    

CREATE INDEX IX ON SomeTable (strID ) INCLUDE (ID, SomeColumn1, SomeColumn2);

SELECT *
FROM ThirdPartyView
WHERE ID = N'1';
_

enter image description here

インデックス付きビュー

問題の制限を考えると、計算された列のアイデアは除外されているようです。代わりの方法は、インデックス付きビューを作成することですが、これを機能させるには、クエリテキストを変更する必要があります。

  • Enterprise Editionを除くすべてのエディションでは、インデックス付きビューを一致させるためにNOEXPANDヒントが必要です。
  • Enterprise Editionでareの場合、元のビューにインデックスを付けた場合でも、コピーを作成した場合でも、自動マッチングは原則的に機能します...
  • ...しかし、インデックスビューの一致は、最適化の後の段階でのみ考慮されます。クエリが安価であれば、最適化はそのポイントに到達する前に終了します。特にあなたのケースでは、あなたは些細な計画と戦う必要もあります。 100万行のデータをテーブルに追加しましたが、インデックス付きビューはまだヒットしていませんでした。プランが並列処理のコストのしきい値を下回り、それ以上の最適化フェーズに進んでいないためです。
9
Martin Smith

サードパーティのビューを変更したり、名前を変更して独自のビューを作成したりして、この愚かな変換を実行しないようにしてください。サポート契約に違反せずにビューにインデックスを追加できる場合は、ビューの定義を変更できます。

_ALTER VIEW dbo.ThirdPartyView
AS
SELECT ID
    ,C1 = SomeColumn1
    ,C2 = SomeColumn2
FROM SomeTable;
_

データ型の優先ルールのおかげで、シークを取得するためにクエリを変更する必要はありませんが、とにかく変更する必要があります。

_SELECT ID, C1, C2
FROM dbo.ThirdPartyView
WHERE ID = N'1';
_

予定:

enter image description here

アプリケーションでビューのID列のデータ型がNVARCHARとして報告されることが絶対に必要な場合(おそらくバインド目的で)、ビュー定義をさらに変更して、selectリストのIDを次のように定義できます。 CAST(ID AS NVARCHAR(10)) AS ID

_ALTER VIEW dbo.ThirdPartyView
AS
SELECT
    ID = CONVERT(nvarchar(10), ID),
    C1 = SomeColumn1,
    C2 = SomeColumn2
FROM SomeTable;
_
4
Aaron Bertrand