wake-up-neo.com

Warum ist LINQ JOIN so viel schneller als das Verknüpfen mit WHERE?

Ich habe kürzlich ein Upgrade auf VS 2010 durchgeführt und spiele mit LINQ to Dataset. Ich habe einen stark typisierten Datensatz für die Autorisierung, der sich in HttpCache einer ASP.NET-Webanwendung befindet.

Also wollte ich wissen, wie man am schnellsten überprüft, ob ein Benutzer berechtigt ist, etwas zu tun. Hier ist mein Datenmodell und einige andere Informationen, wenn jemand interessiert ist.

Ich habe 3 Möglichkeiten überprüft:

  1. direkte Datenbank
  2. LINQ-Abfrage mit Where Bedingungen als "Join" - Syntax
  3. LINQ-Abfrage mit Join - Syntax

Dies sind die Ergebnisse mit 1000 Aufrufen für jede Funktion:

1.Iteration:

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

2.Iteration:

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

3.Iteration:

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

Durchschnitt:

  1. Datenbank: 3,4239506333 Sek.
  2. Wobei: 99,5404964 sek.
  3. Beitritt: 1,815435 Sek.

Warum ist die Join-Version so viel schneller als die Where-Syntax, was sie unbrauchbar macht, obwohl sie als LINQ-Neuling am besten lesbar zu sein scheint. Oder habe ich etwas in meinen Abfragen verpasst?

Hier sind die LINQ-Abfragen, ich überspringe die Datenbank:

Wobei :

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

Join:

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

Danke im Voraus.


Edit : Nach einigen Verbesserungen an beiden Abfragen, um aussagekräftigere Performance-Werte zu erhalten, ist der Vorteil von JOIN sogar um ein Vielfaches größer als zuvor:

Join :

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

Wobei :

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

Ergebnis für 1000 Anrufe (auf einem schnelleren Computer)

  1. Beitreten | 2. Wo

1.Iteration:

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

2.Iteration:

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

3.Iteration:

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

Durchschnitt:

  1. Beitritt: 0,0569367 Sek.
  2. Wobei: 12,8251813 sek.

Der Beitritt ist 225-mal schneller

Fazit: Vermeiden Sie, WO Beziehungen anzugeben, und verwenden Sie JOIN, wann immer dies möglich ist (definitiv in LINQ to DataSet und Linq-To-Objects allgemein).

96
Tim Schmelter
  1. Ihr erster Ansatz (SQL-Abfrage in der Datenbank) ist sehr effizient, da die Datenbank weiß, wie ein Join ausgeführt wird. Ein Vergleich mit den anderen Ansätzen ist jedoch nicht sinnvoll, da diese direkt im Speicher arbeiten (Linq to DataSet).

  2. Die Abfrage mit mehreren Tabellen und einer Where -Bedingung führt tatsächlich ein kartesisches Produkt aller Tabellen aus. dann filtert die Zeilen, die die Bedingung erfüllen. Dies bedeutet, dass die Bedingung Where für jede Kombination von Zeilen ausgewertet wird (n1 * n2 * n3 * n4).

  3. Der Operator Join nimmt die Zeilen aus den ersten Tabellen, dann nur die Zeilen mit einem übereinstimmenden Schlüssel aus der zweiten Tabelle, dann nur die Zeilen mit einem übereinstimmenden Schlüssel aus der dritten Tabelle usw. Dies ist wesentlich effizienter, da nicht so viele Vorgänge ausgeführt werden müssen

75
Thomas Levesque

Das Join ist viel schneller, weil die Methode weiß, wie man die Tabellen kombiniert, um das Ergebnis auf die relevanten Kombinationen zu reduzieren. Wenn Sie die Beziehung mit Where angeben, muss jede mögliche Kombination erstellt und anschließend die Bedingung getestet werden, um festzustellen, welche Kombinationen relevant sind.

Die Join -Methode kann eine Hash-Tabelle einrichten, die als Index verwendet wird, um schnell zwei Tabellen zu komprimieren, während die Where -Methode ausgeführt wird, nachdem alle Kombinationen bereits erstellt wurden, sodass sie nicht verwendet werden kann Tricks, um die Kombinationen vorher zu reduzieren.

19
Guffa

was Sie wirklich wissen müssen, ist die SQL, die für die beiden Anweisungen erstellt wurde. Es gibt einige Möglichkeiten, um dorthin zu gelangen, aber die einfachste ist die Verwendung von LinqPad. Über den Abfrageergebnissen befinden sich mehrere Schaltflächen, die sich in sql ändern. Das gibt Ihnen mehr Informationen als alles andere.

Tolle Informationen, die Sie dort geteilt haben.

7
phillip