Il cuore della domanda non è "perché l'ordine è importante con LINQ?". LINQ traduce semplicemente letteralmente senza riordinare. La vera domanda è "perché le due query SQL hanno prestazioni diverse?".
Sono stato in grado di riprodurre il problema inserendo solo 100k righe. In tal caso viene attivato un punto debole nell'ottimizzatore:non riconosce di poter eseguire una ricerca su Colour
a causa della condizione complessa. Nella prima query l'ottimizzatore riconosce il modello e crea una ricerca di indice.
Non c'è alcuna ragione semantica per cui questo dovrebbe essere. Una ricerca su un indice è possibile anche quando si cerca su NULL
. Questo è un punto debole/bug nell'ottimizzatore. Ecco i due piani:
EF cerca di essere utile in questo caso perché presuppone che sia la colonna che la variabile di filtro possano essere null. In tal caso cerca di darti una corrispondenza (che secondo la semantica C# è la cosa giusta).
Ho provato ad annullare l'operazione aggiungendo il seguente filtro:
Colour IS NOT NULL AND @p__linq__0 IS NOT NULL
AND Size IS NOT NULL AND @p__linq__1 IS NOT NULL
Sperando che l'ottimizzatore ora utilizzi tale conoscenza per semplificare la complessa espressione del filtro EF. Non è riuscito a farlo. Se avesse funzionato, lo stesso filtro avrebbe potuto essere aggiunto alla query EF fornendo una soluzione semplice.
Ecco le correzioni che consiglio nell'ordine in cui dovresti provarle:
- Rendi le colonne del database non nulle nel database
- Rendi le colonne non nulle nel modello di dati EF sperando che ciò impedisca a EF di creare la complessa condizione di filtro
- Crea indici:
Colour, Size
e/oSize, Colour
. Rimuovono anche loro problema. - Assicurati che il filtro sia eseguito nell'ordine corretto e lascia un commento al codice
- Prova a utilizzare
INTERSECT
/Queryable.Intersect
per combinare i filtri. Ciò si traduce spesso in diverse forme del piano. - Crea una funzione inline con valori di tabella che esegua il filtraggio. EF può utilizzare tale funzione come parte di una query più ampia
- Scendi all'SQL grezzo
- Utilizza una guida al piano per modificare il piano
Si tratta di soluzioni alternative, non di soluzioni per la causa principale.
Alla fine non sono soddisfatto sia di SQL Server che di EF qui. Entrambi i prodotti dovrebbero essere riparati. Purtroppo, probabilmente non lo saranno e non puoi aspettare nemmeno quello.
Ecco gli script di indice:
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]