Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

A ordem das cláusulas de consulta LINQ deve afetar o desempenho do Entity Framework?


O cerne da questão não é "por que o pedido importa com o LINQ?". LINQ apenas traduz literalmente sem reordenar. A verdadeira questão é "por que as duas consultas SQL têm desempenho diferente?".

Consegui reproduzir o problema inserindo apenas 100 mil linhas. Nesse caso, uma fraqueza no otimizador está sendo acionada:ele não reconhece que pode fazer uma busca em Colour devido à condição complexa. Na primeira consulta, o otimizador reconhece o padrão e cria uma busca de índice.

Não há nenhuma razão semântica para que isso aconteça. Uma busca em um índice é possível mesmo quando busca em NULL . Esta é uma fraqueza/bug no otimizador. Aqui estão os dois planos:



O EF tenta ser útil aqui porque assume que tanto a coluna quanto a variável de filtro podem ser nulas. Nesse caso, ele tenta fornecer uma correspondência (o que, de acordo com a semântica do C#, é a coisa certa).

Eu tentei desfazer isso adicionando o seguinte filtro:
Colour IS NOT NULL AND @p__linq__0 IS NOT NULL
AND Size IS NOT NULL AND @p__linq__1 IS NOT NULL

Esperando que o otimizador agora use esse conhecimento para simplificar a complexa expressão do filtro EF. Não conseguiu fazê-lo. Se isso tivesse funcionado, o mesmo filtro poderia ter sido adicionado à consulta do EF, fornecendo uma correção fácil.

Aqui estão as correções que eu recomendo na ordem em que você deve experimentá-las:
  1. Torne as colunas do banco de dados não nulas no banco de dados
  2. Torne as colunas não nulas no modelo de dados do EF esperando que isso impeça o EF de criar a condição de filtro complexa
  3. Criar índices:Colour, Size e/ou Size, Colour . Eles também removem o problema.
  4. Certifique-se de que a filtragem seja feita na ordem correta e deixe um comentário de código
  5. Tente usar INTERSECT /Queryable.Intersect para combinar os filtros. Isso geralmente resulta em diferentes formas de plano.
  6. Crie uma função com valor de tabela embutida que faça a filtragem. O EF pode usar essa função como parte de uma consulta maior
  7. Desça para SQL bruto
  8. Use um guia de plano para alterar o plano

Todas essas são soluções alternativas, não correções de causa raiz.

No final, não estou feliz com o SQL Server e o EF aqui. Ambos os produtos devem ser corrigidos. Infelizmente, eles provavelmente não serão e você também não pode esperar por isso.

Aqui estão os scripts de índice:
CREATE NONCLUSTERED INDEX IX_Widget_Colour_Size ON dbo.Widget
    (
    Colour, Size
    ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
CREATE NONCLUSTERED INDEX IX_Widget_Size_Colour ON dbo.Widget
    (
   Size, Colour
    ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]