Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

L'ordre des clauses de requête LINQ doit-il affecter les performances d'Entity Framework ?

Le cœur de la question n'est pas "pourquoi l'ordre est-il important avec LINQ ?". LINQ se traduit simplement littéralement sans réorganiser. La vraie question est "pourquoi les deux requêtes SQL ont-elles des performances différentes ?".

J'ai pu reproduire le problème en insérant seulement 100k lignes. Dans ce cas, une faiblesse de l'optimiseur est déclenchée :il ne reconnaît pas qu'il peut effectuer une recherche sur Colour en raison de la condition complexe. Dans la première requête, l'optimiseur reconnaît le modèle et crée une recherche d'index.

Il n'y a aucune raison sémantique pour laquelle cela devrait être. Une recherche sur un index est possible même en recherchant sur NULL . Il s'agit d'une faiblesse/bogue dans l'optimiseur. Voici les deux forfaits :

EF essaie d'être utile ici car il suppose que la colonne et la variable de filtre peuvent être nulles. Dans ce cas, il essaie de vous donner une correspondance (ce qui, selon la sémantique C#, est la bonne chose).

J'ai essayé d'annuler cela en ajoutant le filtre suivant :

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

En espérant que l'optimiseur utilise maintenant ces connaissances pour simplifier l'expression complexe du filtre EF. Il n'y est pas parvenu. Si cela avait fonctionné, le même filtre aurait pu être ajouté à la requête EF, fournissant une solution simple.

Voici les correctifs que je recommande dans l'ordre dans lequel vous devriez les essayer :

  1. Rendre les colonnes de la base de données non nulles dans la base de données
  2. Rendez les colonnes non nulles dans le modèle de données EF en espérant que cela empêchera EF de créer la condition de filtre complexe
  3. Créer des index :Colour, Size et/ou Size, Colour . Ils suppriment également leur problème.
  4. Assurez-vous que le filtrage est effectué dans le bon ordre et laissez un commentaire de code
  5. Essayez d'utiliser INTERSECT /Queryable.Intersect combiner les filtres. Cela se traduit souvent par des formes de plan différentes.
  6. Créez une fonction table en ligne qui effectue le filtrage. EF peut utiliser une telle fonction dans le cadre d'une requête plus importante
  7. Déroulez jusqu'au SQL brut
  8. Utilisez un guide de plan pour modifier le plan

Toutes ces solutions sont des solutions de contournement, et non des correctifs de cause première.

En fin de compte, je ne suis pas satisfait à la fois de SQL Server et d'EF ici. Les deux produits doivent être corrigés. Hélas, ils ne le seront probablement pas et vous ne pouvez pas attendre non plus.

Voici les scripts d'index :

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]