El núcleo de la pregunta no es "¿por qué importa el orden con LINQ?". LINQ simplemente traduce literalmente sin reordenar. La verdadera pregunta es "¿por qué las dos consultas SQL tienen un rendimiento diferente?".
Pude reproducir el problema insertando solo 100k filas. En ese caso se está disparando una debilidad en el optimizador:no reconoce que puede hacer una búsqueda en Colour
debido a la condición compleja. En la primera consulta, el optimizador reconoce el patrón y crea una búsqueda de índice.
No hay ninguna razón semántica por la que esto debería ser así. Una búsqueda en un índice es posible incluso cuando se busca en NULL
. Esta es una debilidad/error en el optimizador. Aquí están los dos planes:
EF intenta ser útil aquí porque asume que tanto la columna como la variable de filtro pueden ser nulas. En ese caso, intenta darte una coincidencia (que según la semántica de C# es lo correcto).
Intenté deshacer eso agregando el siguiente filtro:
Colour IS NOT NULL AND @p__linq__0 IS NOT NULL
AND Size IS NOT NULL AND @p__linq__1 IS NOT NULL
Con la esperanza de que el optimizador ahora use ese conocimiento para simplificar la compleja expresión del filtro EF. No logró hacerlo. Si esto hubiera funcionado, se podría haber agregado el mismo filtro a la consulta EF, proporcionando una solución fácil.
Estas son las soluciones que recomiendo en el orden en que debe probarlas:
- Haga que las columnas de la base de datos no sean nulas en la base de datos
- Haga que las columnas no sean nulas en el modelo de datos de EF con la esperanza de que esto evite que EF cree la condición de filtro complejo
- Crear índices:
Colour, Size
y/oSize, Colour
. También les quitan el problema. - Asegúrese de que el filtrado se realice en el orden correcto y deje un comentario de código
- Intenta usar
INTERSECT
/Queryable.Intersect
para combinar los filtros. Esto a menudo da como resultado diferentes formas de planta. - Cree una función con valores de tabla en línea que realice el filtrado. EF puede usar dicha función como parte de una consulta más grande
- Desplácese hasta SQL sin procesar
- Use una guía de plan para cambiar el plan
Todas estas son soluciones alternativas, no correcciones de causa raíz.
Al final, no estoy contento con SQL Server y EF aquí. Ambos productos deben ser reparados. Por desgracia, es probable que no lo sean y tampoco puedes esperar por eso.
Aquí están los 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]