97 votes

Pourquoi LINQ JOIN est-il tellement plus rapide que la liaison avec WHERE ?

Je suis récemment passé à VS 2010 et je m'amuse avec LINQ to Dataset. J'ai un jeu de données fortement typé pour l'autorisation qui est dans HttpCache d'une WebApplication ASP.NET.

Je voulais donc savoir quel est le moyen le plus rapide de vérifier si un utilisateur est autorisé à faire quelque chose. Ici est mon modèle de données et quelques autres informations si quelqu'un est intéressé.

J'ai vérifié de 3 façons :

  1. direct base de données
  2. Requête LINQ avec conditions comme "Join" - Syntaxe
  3. Requête LINQ avec Rejoignez - Syntaxe

Voici les résultats avec 1000 appels sur chaque fonction :

1. itération :

  1. 4,2841519 sec.
  2. 115,7796925 sec.
  3. 2,024749 sec.

2. itération :

  1. 3,1954857 sec.
  2. 84,97047 sec.
  3. 1,5783397 sec.

3. itération :

  1. 2,7922143 sec.
  2. 97,8713267 sec.
  3. 1,8432163 sec.

Moyenne :

  1. Base de données : 3,4239506333 sec.
  2. Où : 99,5404964 sec.
  3. Joindre : 1,815435 sec.

Pourquoi la version Join est-elle tellement plus rapide que la syntaxe where qui la rend inutile bien qu'en tant que novice de LINQ, elle semble être la plus lisible. Ou ai-je manqué quelque chose dans mes requêtes ?

Voici les requêtes LINQ, je saute la base de données :

:

Public Function hasAccessDS_Where(ByVal accessRule As String) As Boolean
    Dim userID As Guid = DirectCast(Membership.GetUser.ProviderUserKey, Guid)
    Dim query = From accRule In Authorization.dsAuth.aspnet_AccessRule, _
                roleAccRule In Authorization.dsAuth.aspnet_RoleAccessRule, _
                role In Authorization.dsAuth.aspnet_Roles, _
                userRole In Authorization.dsAuth.aspnet_UsersInRoles _
                Where accRule.idAccessRule = roleAccRule.fiAccessRule _
                And roleAccRule.fiRole = role.RoleId _
                And userRole.RoleId = role.RoleId _
                And userRole.UserId = userID And accRule.RuleName.Contains(accessRule)
                Select accRule.idAccessRule
    Return query.Any
End Function

Rejoignez-nous :

Public Function hasAccessDS_Join(ByVal accessRule As String) As Boolean
    Dim userID As Guid = DirectCast(Membership.GetUser.ProviderUserKey, Guid)
    Dim query = From accRule In Authorization.dsAuth.aspnet_AccessRule _
                Join roleAccRule In Authorization.dsAuth.aspnet_RoleAccessRule _
                On accRule.idAccessRule Equals roleAccRule.fiAccessRule _
                Join role In Authorization.dsAuth.aspnet_Roles _
                On role.RoleId Equals roleAccRule.fiRole _
                Join userRole In Authorization.dsAuth.aspnet_UsersInRoles _
                On userRole.RoleId Equals role.RoleId _
                Where userRole.UserId = userID And accRule.RuleName.Contains(accessRule)
                Select accRule.idAccessRule
    Return query.Any
End Function

Merci d'avance.


Editar Après quelques améliorations sur les deux requêtes pour obtenir des valeurs de performance plus significatives, l'avantage du JOIN est encore beaucoup plus grand qu'avant :

Rejoignez :

Public Overloads Shared Function hasAccessDS_Join(ByVal userID As Guid, ByVal idAccessRule As Int32) As Boolean
    Dim query = From accRule In Authorization.dsAuth.aspnet_AccessRule _
                   Join roleAccRule In Authorization.dsAuth.aspnet_RoleAccessRule _
                   On accRule.idAccessRule Equals roleAccRule.fiAccessRule _
                   Join role In Authorization.dsAuth.aspnet_Roles _
                   On role.RoleId Equals roleAccRule.fiRole _
                   Join userRole In Authorization.dsAuth.aspnet_UsersInRoles _
                   On userRole.RoleId Equals role.RoleId _
                   Where accRule.idAccessRule = idAccessRule And userRole.UserId = userID
             Select role.RoleId
    Return query.Any
End Function

:

Public Overloads Shared Function hasAccessDS_Where(ByVal userID As Guid, ByVal idAccessRule As Int32) As Boolean
    Dim query = From accRule In Authorization.dsAuth.aspnet_AccessRule, _
           roleAccRule In Authorization.dsAuth.aspnet_RoleAccessRule, _
           role In Authorization.dsAuth.aspnet_Roles, _
           userRole In Authorization.dsAuth.aspnet_UsersInRoles _
           Where accRule.idAccessRule = roleAccRule.fiAccessRule _
           And roleAccRule.fiRole = role.RoleId _
           And userRole.RoleId = role.RoleId _
           And accRule.idAccessRule = idAccessRule And userRole.UserId = userID
           Select role.RoleId
    Return query.Any
End Function

Résultat pour 1000 appels (sur un ordinateur plus rapide)

  1. Rejoindre | 2. où

1. itération :

  1. 0,0713669 sec.
  2. 12,7395299 sec.

2. itération :

  1. 0,0492458 sec.
  2. 12,3885925 sec.

3. itération :

  1. 0,0501982 sec.
  2. 13,3474216 sec.

Moyenne :

  1. Joindre : 0,0569367 sec.
  2. Où : 12,8251813 sec.

La jointure est 225 fois plus rapide

Conclusion : éviter WHERE pour spécifier les relations et utiliser JOIN chaque fois que possible (définitivement dans LINQ à DataSet y Linq-To-Objects en général).

75voto

Thomas Levesque Points 141081
  1. Votre première approche (requête SQL dans la BD) est assez efficace car la BD sait comment effectuer une jointure. Mais cela n'a pas vraiment de sens de la comparer avec les autres approches, puisqu'elles travaillent directement en mémoire (Linq to DataSet).

  2. La requête avec plusieurs tables et un Where effectue en fait une produit cartésien de toutes les tables, puis filtre les lignes qui satisfont à la condition. Cela signifie que les Where la condition est évaluée pour chaque combinaison de rangs (n1 * n2 * n3 * n4)

  3. Le site Join prend les lignes du premier tableau, puis ne prend que les lignes avec une clé correspondante du deuxième tableau, puis seulement les lignes avec une clé correspondante du troisième tableau, et ainsi de suite. Cette méthode est beaucoup plus efficace, car elle n'a pas besoin d'effectuer autant d'opérations.

19voto

Guffa Points 308133

Le site Join est beaucoup plus rapide, car la méthode sait comment combiner les tableaux pour réduire le résultat aux combinaisons pertinentes. Lorsque vous utilisez Where pour spécifier la relation, il doit créer toutes les combinaisons possibles, puis tester la condition pour voir quelles combinaisons sont pertinentes.

Le site Join permet de configurer une table de hachage à utiliser comme index pour relier rapidement deux tables, tandis que la méthode Where La méthode s'exécute une fois que toutes les combinaisons sont déjà créées, elle ne peut donc pas utiliser d'astuces pour réduire les combinaisons au préalable.

7voto

phillip Points 1501

Ce que vous devez vraiment savoir est le sql qui a été créé pour les deux déclarations. Il existe plusieurs façons de l'obtenir, mais la plus simple est d'utiliser LinqPad. Il y a plusieurs boutons juste au-dessus des résultats de la requête qui permettent d'afficher le fichier sql. Cela vous donnera beaucoup plus d'informations qu'autre chose.

Mais c'est une excellente information que vous avez partagée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X