J'ai récemment mis à niveau vers VS 2010 et je joue avec LINQ to Dataset. J'ai un ensemble de données fortement typé pour l'autorisation qui se trouve dans HttpCache d'une application Web ASP.NET.
Je voulais donc savoir quel est en fait le moyen le plus rapide de vérifier si un utilisateur est autorisé à faire quelque chose. Voici mon datamodel et quelques autres informations si quelqu'un est intéressé.
J'ai vérifié 3 façons:
- base de données directe
- Requête LINQ avec des conditions Where comme "Join" - Syntaxe
- Requête LINQ avec Join - Syntaxe
Voici les résultats avec 1000 appels sur chaque fonction:
1. itération:
- 4,2841519 sec.
- 115,7796925 sec.
- 2,024749 sec.
2. itération:
- 3,1954857 sec.
- 84 97047 sec.
- 1,5783397 sec.
3. itération:
- 2,7922143 sec.
- 97,8713267 sec.
- 1,8432163 sec.
Moyenne:
- Base de données: 3,4239506333 sec.
- Où: 99,5404964 sec.
- Rejoindre: 1,815435 sec.
Pourquoi la version Join est-elle tellement plus rapide que la syntaxe where, ce qui la rend inutile alors qu'en tant que débutant 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:
Où :
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
Joindre:
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.
Edit : après quelques améliorations sur les deux requêtes pour obtenir des valeurs de performance plus significatives, l'avantage du JOIN est même plusieurs fois plus important qu'avant:
Rejoindre :
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
Où :
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)
- Rejoindre | 2. Où
1. itération:
- 0,0713669 sec.
- 12,7395299 sec.
2. itération:
- 0,0492458 sec.
- 12,3885925 sec.
3. itération:
- 0,0501982 sec.
- 13,3474216 sec.
Moyenne:
- Joindre: 0,0569367 sec.
- Où: 12,8251813 sec.
L'adhésion est 225 fois plus rapide
Conclusion: évitez WHERE de spécifier des relations et utilisez JOIN dans la mesure du possible (certainement dans LINQ to DataSet et Linq-To-Objects
en général).
la source
Join
anywhy, pourquoi compter sur un optimiseur si vous pouvez écrire le code optimisé depuis le début? Cela rend également vos intentions plus claires. Donc, les mêmes raisons pour lesquelles vous devriez préférer JOIN en sql .Réponses:
Votre première approche (requête SQL dans la base de données) est assez efficace car la base de données sait comment effectuer une jointure. Mais cela n'a pas vraiment de sens de le comparer avec les autres approches, car ils fonctionnent directement en mémoire (Linq to DataSet)
La requête avec plusieurs tables et une
Where
condition effectue en fait un produit cartésien de toutes les tables, puis filtre les lignes qui satisfont à la condition. Cela signifie que laWhere
condition est évaluée pour chaque combinaison de lignes (n1 * n2 * n3 * n4)L'
Join
opérateur prend les lignes des premières tables, puis prend uniquement les lignes avec une clé correspondante de la deuxième table, puis uniquement les lignes avec une clé correspondante de la troisième table, et ainsi de suite. C'est beaucoup plus efficace, car il n'est pas nécessaire d'effectuer autant d'opérationsla source
where
-query d'une manière ou d'une autre comme un dbms. En fait,JOIN
c'était même 225 fois plus rapide que laWHERE
(dernière modification).Le
Join
est beaucoup plus rapide, car la méthode sait combiner les tableaux pour réduire le résultat aux combinaisons pertinentes. Lorsque vous utilisezWhere
pour spécifier la relation, il doit créer toutes les combinaisons possibles, puis tester la condition pour voir quelles combinaisons sont pertinentes.La
Join
méthode peut configurer une table de hachage à utiliser comme index pour compresser rapidement deux tables ensemble, tandis que laWhere
méthode s'exécute une fois que toutes les combinaisons sont déjà créées, de sorte qu'elle ne peut utiliser aucune astuce pour réduire les combinaisons au préalable.la source
join
mot - clé, car il n'y a pas d'analyse d'exécution de la requête pour produire quelque chose d'analogue à un plan d'exécution. Vous remarquerez également que les jointures basées sur LINQ ne peuvent accueillir que des équidés à une seule colonne.... on new { f1.Key1, f1.Key2 } equals new { f2.Key1, f2.Key2 }
ce que vous devez vraiment savoir, c'est le SQL qui a été créé pour les deux instructions. Il existe plusieurs moyens d'y parvenir, mais le plus simple est d'utiliser LinqPad. Il y a plusieurs boutons juste au-dessus des résultats de la requête qui changeront en sql. Cela vous donnera beaucoup plus d'informations qu'autre chose.
Excellente information que vous avez partagée là-bas.
la source