Jádrem otázky není "proč na pořadí u LINQ záleží?". LINQ pouze překládá doslovně bez změny pořadí. Skutečná otázka zní:„Proč mají tyto dva dotazy SQL různý výkon?“.
Byl jsem schopen reprodukovat problém pouze vložením 100 000 řádků. V takovém případě se spouští slabá stránka optimalizátoru:nerozpozná, že může provést vyhledávání na Colour
kvůli složitému stavu. V prvním dotazu optimalizátor rozpozná vzor a vytvoří hledání indexu.
Neexistuje žádný sémantický důvod, proč by tomu tak mělo být. Hledání na indexu je možné i při hledání na NULL
. Toto je slabina/chyba v optimalizátoru. Zde jsou dva plány:
EF se zde snaží být nápomocný, protože předpokládá, že sloupec i proměnná filtru mohou mít hodnotu null. V takovém případě se vám pokusí poskytnout shodu (což je podle sémantiky C# správná věc).
Pokusil jsem se to vrátit zpět přidáním následujícího filtru:
Colour IS NOT NULL AND @p__linq__0 IS NOT NULL
AND Size IS NOT NULL AND @p__linq__1 IS NOT NULL
Doufáme, že optimalizátor nyní tyto znalosti využije ke zjednodušení komplexního výrazu filtru EF. Nepodařilo se tak učinit. Pokud by to fungovalo, stejný filtr by mohl být přidán do dotazu EF, což by poskytlo snadnou opravu.
Zde jsou opravy, které doporučuji v pořadí, v jakém byste je měli vyzkoušet:
- Nastavte, aby sloupce databáze nebyly v databázi null
- Nastavte sloupce bez nuly v datovém modelu EF doufat, že to zabrání EF ve vytvoření složité podmínky filtru
- Vytvořte indexy:
Colour, Size
a/neboSize, Colour
. Odstraňují také problém. - Zkontrolujte, zda je filtrování provedeno ve správném pořadí, a zanechte komentář ke kódu
- Zkuste použít
INTERSECT
/Queryable.Intersect
ke kombinaci filtrů. To často vede k různým půdorysným tvarům. - Vytvořte funkci vložené tabulky s hodnotou, která provádí filtrování. EF může takovou funkci použít jako součást většího dotazu
- Rozbalte na nezpracovaný SQL
- Změňte plán pomocí průvodce plánem
To vše jsou zástupná řešení, nikoli opravy hlavních příčin.
Nakonec nejsem spokojený s SQL Serverem a EF zde. Oba produkty by měly být opraveny. Bohužel, pravděpodobně nebudou a vy se toho také nemůžete dočkat.
Zde jsou indexové skripty:
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]