existe-t-il un moyen de contourner le problème lorsque vous souhaitez placer un OU dans un index filtré?

8

existe-t-il un moyen de contourner le problème lorsque vous souhaitez placer un OU dans un index filtré?

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

J'essaie de créer l'index ci-dessus, car je ne suis PAS intéressé par une situation où sintOrderStatusId IN (9-14)

Bien sûr, je peux créer une vue ou une vue indexée, mais j'essayais d'éviter cela.

juste en ajoutant plus d'informations: sintOrderStatusId est un smallint NOT NULL et les valeurs possibles vont de 1 à 30. les 9 à 14 sont à éviter, donc l'index filtré.

Marcello Miorelli
la source

Réponses:

12

Malheureusement, il semble qu'il n'y ait aucun moyen de créer un filtre négatif pour un index, sans recourir à la création d'une vue matérialisée. S'il était possible de créer un filtre négatif tel que celui que vous souhaitez, il serait assez difficile pour l'optimiseur de requêtes de "choisir" l'index à utiliser, augmentant considérablement le temps nécessaire pour trouver un bon plan.

Selon les modèles de requête pour cette table, vous pouvez simplement créer deux index; un pour moins de 9 et un pour plus de 14. L'un ou l'autre de ces index peut être choisi par l'optimiseur de requête pour des WHEREclauses simples telles queWHERE 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);

Une autre façon d'y parvenir pourrait être:

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

Cela utilise l'index filtré sur 9 à 14 pour exclure les lignes.

Sur mon banc d'essai, un simple indice de recouvrement renvoie les lignes de loin le plus rapide:

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

Alternativement, en utilisant une variation de l'approche utilisée dans votre propre réponse :

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;

Bien que le filtre soit écrit sous forme de conjonctions, il prend en charge les requêtes écrites de l'une des manières suivantes (la première étant légèrement plus efficace):

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

pas génial, mais semble fonctionner:

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

Quand j'essaie de le faire d'une meilleure façon, ça ne me plaît pas:

entrez la description de l'image ici

Marcello Miorelli
la source
1
Selon la documentation MSND: "Les index filtrés sont définis sur une seule table et ne prennent en charge que les opérateurs de comparaison simples. Si vous avez besoin d'une expression de filtre qui référence plusieurs tables ou a une logique complexe, vous devez créer une vue." msdn.microsoft.com/en-us/library/cc280372.aspx
Paweł Tajs