De kern van de vraag is niet "waarom doet de volgorde er toe bij LINQ?". LINQ vertaalt gewoon letterlijk zonder opnieuw te ordenen. De echte vraag is "waarom hebben de twee SQL-query's verschillende prestaties?".
Ik kon het probleem reproduceren door slechts 100k rijen in te voegen. In dat geval wordt een zwakte in de optimizer geactiveerd:deze herkent niet dat hij kan zoeken op Colour
vanwege de complexe toestand. In de eerste zoekopdracht herkent de optimizer het patroon en creëert een indexzoekopdracht.
Er is geen semantische reden waarom dit zou moeten. Zoeken op een index is zelfs mogelijk als u zoekt op NULL
. Dit is een zwakte/bug in de optimizer. Dit zijn de twee plannen:
EF probeert hier behulpzaam te zijn omdat het ervan uitgaat dat zowel de kolom als de filtervariabele null kan zijn. In dat geval probeert het je een match te geven (wat volgens de semantiek van C# de juiste is).
Ik heb geprobeerd dat ongedaan te maken door het volgende filter toe te voegen:
Colour IS NOT NULL AND @p__linq__0 IS NOT NULL
AND Size IS NOT NULL AND @p__linq__1 IS NOT NULL
In de hoop dat de optimizer die kennis nu gebruikt om de complexe EF-filterexpressie te vereenvoudigen. Het is daar niet in geslaagd. Als dit had gewerkt, had hetzelfde filter aan de EF-query kunnen worden toegevoegd, wat een gemakkelijke oplossing bood.
Dit zijn de oplossingen die ik aanbeveel in de volgorde waarin u ze moet proberen:
- Maak de databasekolommen niet-null in de database
- Maak de kolommen niet-null in het EF-gegevensmodel in de hoop dat dit zal voorkomen dat EF de complexe filtervoorwaarde creëert
- Maak indexen:
Colour, Size
en/ofSize, Colour
. Ze verwijderen ook het probleem. - Zorg ervoor dat de filtering in de juiste volgorde gebeurt en laat een codecommentaar achter
- Probeer
INTERSECT
. te gebruiken /Queryable.Intersect
om de filters te combineren. Dit resulteert vaak in verschillende plattegrondvormen. - Maak een inline tabelwaardefunctie die de filtering doet. EF kan zo'n functie gebruiken als onderdeel van een grotere zoekopdracht
- Drop naar onbewerkte SQL
- Gebruik een plangids om het plan te wijzigen
Dit zijn allemaal tijdelijke oplossingen, geen oplossingen voor de oorzaak.
Uiteindelijk ben ik hier niet blij met zowel SQL Server als EF. Beide producten moeten worden gerepareerd. Helaas zullen ze dat waarschijnlijk niet zijn en daar kun jij ook niet op wachten.
Dit zijn de indexscripts:
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]