Есть ли обходной путь, когда вы хотите поместить ИЛИ в фильтруемый индекс?

8

Есть ли обходной путь, когда вы хотите поместить ИЛИ в фильтруемый индекс?

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

К сожалению, похоже, нет способа создать отрицательный фильтр для индекса, не прибегая к созданию материализованного представления. Если бы было возможно создать отрицательный фильтр, такой как тот, который вам нужен, оптимизатору запросов было бы довольно сложно «выбрать» индекс для использования, что резко увеличило бы время, необходимое для поиска хорошего плана.

В зависимости от шаблонов запросов для этой таблицы вы можете просто создать два индекса; один для менее чем 9 и один для более чем 14. Любой из этих индексов может быть выбран оптимизатором запросов для 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: «Отфильтрованные индексы определены для одной таблицы и поддерживают только простые операторы сравнения. Если вам нужно выражение фильтра, которое ссылается на несколько таблиц или имеет сложную логику, вам следует создать представление». msdn.microsoft.com/en-us/library/cc280372.aspx
Павел Тайс