フィルターされたインデックス内にORを配置する場合の回避策はありますか?


8

フィルターされたインデックス内にORを配置する場合の回避策はありますか?

create index FIDX_tblbOrders_sdtmOrdCreated_INCL 
on dbo.tblBOrder(sdtmOrdCreated)
INCLUDE (sintMarketID,
         strCurrencyCode,
         sintOrderStatusID
         )
WHERE ((sintMarketId=1)
AND ( (sintOrderStatusId < 9) OR (sintOrderStatusId > 14)))

上記のインデックスを作成しようとしています。sintOrderStatusIdがIN(9-14)である状況には興味がないためです。

もちろん、ビューやインデックス付きビューを作成することはできますが、それを避けようとしていました。

情報を追加するだけ:sintOrderStatusIdはsmallint NOT NULLであり、可能な値の範囲は1〜30です。9〜14は避けてください。したがって、フィルターされたインデックスです。

回答:


12

残念ながら、マテリアライズドビューの作成に頼らずにインデックスの否定フィルターを作成する方法はないようです。希望するフィルターなどの否定的なフィルターを作成することできた場合、クエリオプティマイザーが使用するインデックスを "選択"することは非常に難しく、適切なプランを見つけるために必要な時間が大幅に増加します。

このテーブルのクエリパターンに応じて、単純に2つのインデックスを作成できます。これらのインデックスの14いずれか未満9と大きいための1つのための1つができる単純なため、クエリ・オプティマイザによって選択されるWHEREような条項WHERE StatusID = 6

CREATE TABLE dbo.TestNegativeFilter
(
    TestNegativeFilter INT NOT NULL
        CONSTRAINT PK_TestNegativeFilter
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , StatusID INT NOT NULL
);
GO

CREATE INDEX IX_TestNagativeFilter_LessThan9
ON dbo.TestNegativeFilter(StatusID)
WHERE (StatusID < 9);

CREATE INDEX IX_TestNagativeFilter_GreaterThan14
ON dbo.TestNegativeFilter(StatusID)
WHERE (StatusID > 14);

これを実現する別の方法は次のとおりです。

CREATE INDEX IX_TestNegativeFilter_9_to_14
ON dbo.TestNegativeFilter(StatusID)
WHERE (StatusID IN (9, 10, 11, 12, 13, 14));

SELECT *
FROM dbo.TestNegativeFilter tnf
EXCEPT
SELECT *
FROM dbo.TestNegativeFilter tnf
WHERE tnf.StatusID IN (9, 10, 11, 12, 13, 14);

これは、行を除外するために9から14でフィルタリングされたインデックスを使用します。

私のテストリグでは、単純なカバリングインデックスが行を最も速く返します。

CREATE NONCLUSTERED INDEX IX_TestNegativeFilter_StatusID
ON dbo.TestNegativeFilter(StatusID)
INCLUDE (TestNegativeFilter);

SELECT *
FROM dbo.TestNegativeFilter tnf
WHERE tnf.StatusID NOT IN (9, 10, 11, 12, 13, 14);

あるいは、あなた自身の答えで使用されいるアプローチのバリエーションを使用してください

CREATE INDEX [IX dbo.TestNegativeFilter StatusID not 9-14]
ON dbo.TestNegativeFilter (StatusID)
WHERE StatusID <> 9
AND StatusID <> 10
AND StatusID <> 11
AND StatusID <> 12
AND StatusID <> 13
AND StatusID <> 14;

フィルタは論理積として記述されていますが、次のいずれかの方法で記述されたクエリをサポートしています(最初の方が少し効率的です)。

  • StatusID NOT IN (9, 10, 11, 12, 13, 14)
  • StatusID < 9 OR StatusID > 14
  • StatusID NOT BETWEEN 9 AND 14

1

すばらしいわけではありませんが、機能しているようです:

create index FIDX_tblbOrders_sdtmOrdCreated_INCL 
on dbo.tblBOrder(sdtmOrdCreated)
INCLUDE (sintMarketID,
         strCurrencyCode,
         sintOrderStatusID
         )
WHERE ((sintMarketId=1)
AND ( sintOrderStatusId IN (0,1,2,3,4,5,6,7,8,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30)))

私がより良い方法でそれをしようとすると、それは好きではありません:

ここに画像の説明を入力してください


1
MSNDのドキュメントによれば、「フィルターされたインデックスは1つのテーブルで定義され、単純な比較演算子のみをサポートします。複数のテーブルを参照する、または複雑なロジックを持つフィルター式が必要な場合は、ビューを作成する必要があります。」msdn.microsoft.com/en-us/library/cc280372.aspx
パヴェルTajs
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.