Perché LINQ JOIN molto più veloce di un collegamento con WHERE? [.net]


Answers

Il Join è molto più veloce, perché il metodo sa come combinare le tabelle per ridurre il risultato alle relative combinazioni. Quando si utilizza Where specificare la relazione, è necessario creare tutte le combinazioni possibili e quindi verificare la condizione per vedere quali combinazioni sono rilevanti.

Il metodo di Join può impostare una tabella di hash da utilizzare come indice per la combinazione rapida di due tabelle congiuntamente, mentre il metodo Where viene eseguito dopo che tutte le combinazioni sono già state create, quindi non è possibile utilizzare alcun trucco per ridurre in anticipo le combinazioni.

Question

Ho recentemente aggiornato a VS 2010 e sto giocando con LINQ a Dataset. Ho un set di dati forti per l'autorizzazione che è in HttpCache di una ASP.NET WebApplication.

Quindi volevo sapere che cosa è effettivamente il modo più veloce per verificare se un utente è autorizzato a fare qualcosa. Ecco il mio modello di dati e alcune altre informazioni se qualcuno è interessato.

Ho controllato 3 modi:

  1. Database diretto
  2. LINQ query con Dove condizioni come "Join" - Sintassi
  3. LINQ query con Join - Sintassi

Questi sono i risultati con 1000 chiamate su ciascuna funzione:

1.Iteration:

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

2.Iteration:

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

3.Iteration:

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

Media:

  1. Database: 3,4239506333 sec.
  2. Dove: 99,5404964 sec.
  3. Unisciti: 1,815435 sec.

Perché la versione di Join è molto più veloce della sintassi dove la sintassi lo rende inutile anche se sembra essere il più leggibile come un newbie di LINQ. Oppure ho perso qualcosa nelle mie query?

Ecco le query LINQ, salto il database:

Dove :

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

Aderire:

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

Grazie in anticipo.

Modifica : dopo alcuni miglioramenti su entrambe le query per ottenere valori di perfomance più significativi, il vantaggio del JOIN è addirittura più volte superiore a quello precedente:

Iscriviti :

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

Dove :

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

Risultato per 1000 chiamate (su un computer più veloce)

  1. Iscriviti | 2. Dove

1.Iteration:

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

2.Iteration:

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

3.Iteration:

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

Media:

  1. Registrati: 0,0569367 sec.
  2. Dove: 12,8251813 sec.

Il join è 225 volte più veloce

Conclusione: evitare dove specificare le relazioni e utilizzare JOIN ogniqualvolta sia possibile (sicuramente in LINQ a DataSet e Linq-To-Objects in generale).