Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Sollte sich die Reihenfolge der LINQ-Abfrageklauseln auf die Leistung von Entity Framework auswirken?

Der Kern der Frage ist nicht "Warum spielt die Reihenfolge bei LINQ eine Rolle?". LINQ übersetzt einfach wörtlich ohne Neuordnung. Die eigentliche Frage lautet:"Warum haben die beiden SQL-Abfragen eine unterschiedliche Leistung?".

Ich konnte das Problem reproduzieren, indem ich nur 100.000 Zeilen einfügte. In diesem Fall wird eine Schwachstelle im Optimierer ausgelöst:Er erkennt nicht, dass er nach Colour suchen kann aufgrund des komplexen Zustands. Bei der ersten Abfrage erkennt der Optimierer das Muster und erstellt eine Indexsuche.

Es gibt keinen semantischen Grund, warum dies so sein sollte. Eine Suche auf einem Index ist sogar möglich, wenn auf NULL gesucht wird . Dies ist eine Schwäche/ein Fehler im Optimierer. Hier sind die beiden Pläne:

EF versucht hier hilfreich zu sein, da davon ausgegangen wird, dass sowohl die Spalte als auch die Filtervariable null sein können. In diesem Fall versucht es, Ihnen eine Übereinstimmung zu geben (was laut C#-Semantik das Richtige ist).

Ich habe versucht, dies rückgängig zu machen, indem ich den folgenden Filter hinzufügte:

Colour IS NOT NULL AND @p__linq__0 IS NOT NULL
AND Size IS NOT NULL AND @p__linq__1 IS NOT NULL

In der Hoffnung, dass der Optimierer dieses Wissen nun nutzt, um den komplexen EF-Filterausdruck zu vereinfachen. Es gelang ihm nicht. Wenn dies funktioniert hätte, hätte derselbe Filter zur EF-Abfrage hinzugefügt werden können, um eine einfache Lösung zu bieten.

Hier sind die Korrekturen, die ich in der Reihenfolge empfehle, in der Sie sie ausprobieren sollten:

  1. Machen Sie die Datenbankspalten in der Datenbank nicht-null
  2. Machen Sie die Spalten im EF-Datenmodell not-null, in der Hoffnung, dass dies EF daran hindert, die komplexe Filterbedingung zu erstellen
  3. Indizes erstellen:Colour, Size und/oder Size, Colour . Sie entfernen auch das Problem.
  4. Stellen Sie sicher, dass die Filterung in der richtigen Reihenfolge erfolgt, und hinterlassen Sie einen Codekommentar
  5. Versuchen Sie, INTERSECT zu verwenden /Queryable.Intersect um die Filter zu kombinieren. Dadurch ergeben sich oft unterschiedliche Grundrissformen.
  6. Erstellen Sie eine Inline-Tabellenwertfunktion, die die Filterung durchführt. EF kann eine solche Funktion als Teil einer größeren Abfrage verwenden
  7. Dropdown zu Raw-SQL
  8. Verwenden Sie eine Planhinweisliste, um den Plan zu ändern

All dies sind Problemumgehungen, keine Ursachenbehebungen.

Am Ende bin ich hier sowohl mit SQL Server als auch mit EF nicht zufrieden. Beide Produkte sollten repariert werden. Leider werden sie es wahrscheinlich nicht sein und darauf können Sie auch nicht warten.

Hier sind die Indexskripte:

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]