existe uma solução alternativa para quando você deseja colocar um OR dentro de um índice filtrado?

8

existe uma solução alternativa para quando você deseja colocar um OR dentro de um índice filtrado?

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

Estou tentando criar o índice acima, porque NÃO estou interessado em nenhuma situação em que sintOrderStatusId IN (9-14)

É claro que posso criar uma exibição ou exibição indexada, mas estava tentando evitar isso.

basta adicionar mais informações: sintOrderStatusId é um número pequeno e NÃO NULL e os valores possíveis variam de 1 a 30. os 9 a 14 devem ser evitados, portanto, o índice filtrado.

Marcello Miorelli
fonte

Respostas:

12

Infelizmente, parece não haver maneira de criar um filtro negativo para um índice, sem recorrer à criação de uma exibição materializada. Se fosse possível criar um filtro negativo como o desejado, seria muito difícil para o otimizador de consultas "escolher" o índice para uso, aumentando drasticamente o tempo necessário para encontrar um bom plano.

Dependendo dos padrões de consulta para esta tabela, você pode simplesmente criar dois índices; um para menos de 9 e um para maior que 14. Qualquer um desses índices pode ser escolhido pelo otimizador de consulta para WHEREcláusulas simples comoWHERE 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);

Outra maneira de conseguir isso pode ser:

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

Isso usa o índice filtrado de 9 a 14 para excluir linhas.

No meu equipamento de teste, um índice de cobertura simples retorna as linhas de longe o mais rápido:

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

Como alternativa, use uma variação da abordagem usada em sua própria resposta :

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;

Apesar de o filtro ser gravado como conjunção, ele suporta consultas gravadas de qualquer uma das seguintes maneiras (a primeira sendo um pouco mais eficiente):

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

não é ótimo, mas parece estar funcionando:

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

Quando tento fazê-lo de uma maneira melhor, ele não gosta:

insira a descrição da imagem aqui

Marcello Miorelli
fonte
11
De acordo com a documentação do MSND: "Os índices filtrados são definidos em uma tabela e suportam apenas operadores de comparação simples. Se você precisar de uma expressão de filtro que faça referência a várias tabelas ou tenha lógica complexa, crie uma exibição". msdn.microsoft.com/pt-br/library/cc280372.aspx
Paweł Tajs