テーブルがあると仮定します。
-- 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'
理由がわかります。
クエリの外でINDEX SEEKを取得するにはどうすればよいですか?
社説/事情:
NVARCHAR
であると想定しています。残念ながらこの条件を満たす必要があります。または私は彼らに会うように言われました。そうでない場合、おそらくアプリケーションは失敗します。問題は、ビューを介したクエリが実行と同じであることです
_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';
_
インデックス付きビュー
問題の制限を考えると、計算された列のアイデアは除外されているようです。代わりの方法は、インデックス付きビューを作成することですが、これを機能させるには、クエリテキストを変更する必要があります。
NOEXPAND
ヒントが必要です。サードパーティのビューを変更したり、名前を変更して独自のビューを作成したりして、この愚かな変換を実行しないようにしてください。サポート契約に違反せずにビューにインデックスを追加できる場合は、ビューの定義を変更できます。
_ALTER VIEW dbo.ThirdPartyView
AS
SELECT ID
,C1 = SomeColumn1
,C2 = SomeColumn2
FROM SomeTable;
_
データ型の優先ルールのおかげで、シークを取得するためにクエリを変更する必要はありませんが、とにかく変更する必要があります。
_SELECT ID, C1, C2
FROM dbo.ThirdPartyView
WHERE ID = N'1';
_
予定:
アプリケーションでビューの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;
_