Kärnan i frågan är inte "varför spelar beställningen roll med LINQ?". LINQ översätter bara bokstavligt utan att ändra ordning. Den verkliga frågan är "varför har de två SQL-frågorna olika prestanda?".
Jag kunde återskapa problemet genom att bara infoga 100k rader. I så fall utlöses en svaghet i optimeraren:den känner inte igen att den kan göra en sökning på Colour
på grund av det komplexa tillståndet. I den första frågan känner optimeraren igen mönstret och skapar en indexsökning.
Det finns ingen semantisk anledning till att det skulle vara så. En sökning på ett index är möjlig även när du söker på NULL
. Detta är en svaghet/bugg i optimeraren. Här är de två planerna:
EF försöker vara till hjälp här eftersom det antar att både kolumnen och filtervariabeln kan vara null. I så fall försöker den ge dig en matchning (vilket enligt C# semantik är det rätta).
Jag försökte ångra det genom att lägga till följande filter:
Colour IS NOT NULL AND @p__linq__0 IS NOT NULL
AND Size IS NOT NULL AND @p__linq__1 IS NOT NULL
Hoppas att optimeraren nu använder den kunskapen för att förenkla det komplexa EF-filteruttrycket. Det lyckades den inte med. Om detta hade fungerat kunde samma filter ha lagts till i EF-frågan, vilket ger en enkel lösning.
Här är korrigeringarna som jag rekommenderar i den ordning du ska prova dem:
- Gör att databaskolumnerna inte är null i databasen
- Gör kolumnerna inte-null i EF-datamodellen och hoppas att detta kommer att förhindra EF från att skapa det komplexa filtervillkoret
- Skapa index:
Colour, Size
och/ellerSize, Colour
. De tar också bort problemen. - Se till att filtreringen görs i rätt ordning och lämna en kodkommentar
- Försök att använda
INTERSECT
/Queryable.Intersect
för att kombinera filtren. Detta resulterar ofta i olika planformer. - Skapa en inline-tabellvärderad funktion som gör filtreringen. EF kan använda en sådan funktion som en del av en större fråga
- Rulla ned till rå SQL
- Använd en planguide för att ändra planen
Alla dessa är lösningar, inte rotorsakskorrigeringar.
I slutändan är jag inte nöjd med både SQL Server och EF här. Båda produkterna bör fixas. Tyvärr kommer de förmodligen inte att vara det och du kan inte vänta på det heller.
Här är indexskripten:
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]