Kernen i spørgsmålet er ikke "hvorfor betyder rækkefølgen noget med LINQ?". LINQ oversætter bare bogstaveligt uden at omarrangere. Det rigtige spørgsmål er "hvorfor har de to SQL-forespørgsler forskellig ydeevne?".
Jeg var i stand til at genskabe problemet ved kun at indsætte 100k rækker. I så fald udløses en svaghed i optimeringsværktøjet:det genkender ikke, at det kan søge på Colour
på grund af den komplekse tilstand. I den første forespørgsel genkender optimeringsværktøjet mønsteret og opretter en indekssøgning.
Der er ingen semantisk grund til, hvorfor dette skulle være. En søgning på et indeks er mulig, selv når du søger på NULL
. Dette er en svaghed/fejl i optimizeren. Her er de to planer:
EF forsøger at være behjælpelig her, fordi den antager, at både kolonnen og filtervariablen kan være nul. I så fald forsøger den at give dig et match (hvilket ifølge C# semantik er det rigtige).
Jeg forsøgte at fortryde det ved at tilføje følgende filter:
Colour IS NOT NULL AND @p__linq__0 IS NOT NULL
AND Size IS NOT NULL AND @p__linq__1 IS NOT NULL
Håber at optimeringsværktøjet nu bruger den viden til at forenkle det komplekse EF-filterudtryk. Det nåede den ikke. Hvis dette havde virket, kunne det samme filter være blevet tilføjet til EF-forespørgslen, hvilket giver en nem løsning.
Her er rettelserne, som jeg anbefaler i den rækkefølge, du bør prøve dem:
- Gør databasekolonnerne til ikke-null i databasen
- Gør kolonnerne til ikke-null i EF-datamodellen i håb om, at dette forhindrer EF i at skabe den komplekse filterbetingelse
- Opret indekser:
Colour, Size
og/ellerSize, Colour
. De fjerner også problemet. - Sørg for, at filtreringen udføres i den rigtige rækkefølge, og efterlad en kodekommentar
- Prøv at bruge
INTERSECT
/Queryable.Intersect
at kombinere filtrene. Dette resulterer ofte i forskellige planformer. - Opret en indlejret tabelværdi-funktion, der udfører filtreringen. EF kan bruge en sådan funktion som en del af en større forespørgsel
- Drop ned til rå SQL
- Brug en planvejledning til at ændre planen
Alle disse er løsninger, ikke rodårsagsrettelser.
I sidste ende er jeg ikke tilfreds med både SQL Server og EF her. Begge produkter skal rettes. Ak, det bliver de sandsynligvis ikke, og det kan du heller ikke vente på.
Her er indeksscripts:
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]