Gibt es eine Problemumgehung, wenn Sie ein ODER in einen gefilterten Index einfügen möchten?

8

Gibt es eine Problemumgehung, wenn Sie ein ODER in einen gefilterten Index einfügen möchten?

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

Ich versuche, den obigen Index zu erstellen, da mich KEINE Situation interessiert, in der sintOrderStatusId IN (9-14)

Natürlich kann ich eine Ansicht oder eine indizierte Ansicht erstellen, aber ich habe versucht, dies zu vermeiden.

nur weitere Infos hinzufügen: sintOrderStatusId ist ein smallint NOT NULL und die möglichen Werte reichen von 1 bis 30. Die 9 bis 14 sind zu vermeiden, daher der gefilterte Index.

Marcello Miorelli
quelle

Antworten:

12

Leider scheint es keine Möglichkeit zu geben, einen Negativfilter für einen Index zu erstellen, ohne eine materialisierte Ansicht zu erstellen. Wenn es ist möglich , einen negativen Filter wie das zu schaffen Sie mögen, wäre es ziemlich schwierig sein , für die Abfrage-Optimierer des Index für die Verwendung „Pick“, drastisch zu erhöhen die Zeit , einen guten Plan zu finden , erforderlich.

Abhängig von den Abfragemustern für diese Tabelle können Sie einfach zwei Indizes erstellen. einer für weniger als 9 und einer für mehr als 14. Jeder dieser Indizes kann vom Abfrageoptimierer für einfache WHEREKlauseln wie zWHERE 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);

Ein anderer Weg, dies zu erreichen, könnte sein:

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);

Dies verwendet den nach 9 bis 14 gefilterten Index, um Zeilen auszuschließen.

Auf meinem Prüfstand gibt ein einfacher Abdeckungsindex Zeilen bei weitem am schnellsten zurück:

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);

Alternativ können Sie eine Variation des in Ihrer eigenen Antwort verwendeten Ansatzes verwenden :

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;

Obwohl der Filter als Konjunktion geschrieben wird, unterstützt er Abfragen, die auf eine der folgenden Arten geschrieben wurden (die erste ist etwas effizienter):

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

nicht großartig, scheint aber zu funktionieren:

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)))

Wenn ich versuche, es besser zu machen, mag es es nicht:

Geben Sie hier die Bildbeschreibung ein

Marcello Miorelli
quelle
1
Laut MSND-Dokumentation: "Gefilterte Indizes werden für eine Tabelle definiert und unterstützen nur einfache Vergleichsoperatoren. Wenn Sie einen Filterausdruck benötigen, der auf mehrere Tabellen verweist oder eine komplexe Logik aufweist, sollten Sie eine Ansicht erstellen." msdn.microsoft.com/en-us/library/cc280372.aspx
Paweł Tajs