Recentemente, atualizei para o VS 2010 e estou brincando com LINQ to Dataset. Eu tenho um forte conjunto de dados tipado para autorização que está em HttpCache de um ASP.NET WebApplication.
Queria saber qual é a maneira mais rápida de verificar se um usuário está autorizado a fazer algo. Aqui está meu modelo de dados e algumas outras informações caso alguém esteja interessado.
Eu verifiquei 3 maneiras:
- banco de dados direto
- Consulta LINQ com condições Where como "Join" - Sintaxe
- Consulta LINQ com Join - Sintaxe
Estes são os resultados com 1000 chamadas em cada função:
1.Iteração:
- 4.2841519 seg.
- 115,7796925 seg.
- 2.024749 seg.
2.Iteração:
- 3,1954857 seg.
- 84.97047 seg.
- 1.5783397 seg.
3.Iteração:
- 2,7922143 seg.
- 97.8713267 seg.
- 1,8432163 seg.
Média:
- Banco de dados: 3,4239506333 seg.
- Onde: 99.5404964 seg.
- Unir: 1.815435 seg.
Por que a versão Join é muito mais rápida do que a sintaxe where, o que a torna inútil, embora, para um novato do LINQ, ela pareça ser a mais legível. Ou eu perdi algo em minhas consultas?
Aqui estão as consultas LINQ, eu pulo o banco de dados:
Onde :
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
Junte-se:
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
Agradeço antecipadamente.
Editar : depois de algumas melhorias em ambas as consultas para obter valores de desempenho mais significativos, a vantagem do JOIN é ainda muitas vezes maior do que antes:
Junte - se :
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
Onde :
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
Resultado para 1000 chamadas (em um computador mais rápido)
- Junte-se | 2. Onde
1.Iteração:
- 0,0713669 seg.
- 12,7395299 seg.
2.Iteração:
- 0,0492458 seg.
- 12,3885925 seg.
3.Iteração:
- 0,0501982 seg.
- 13,3474216 seg.
Média:
- União: 0,0569367 seg.
- Onde: 12,8251813 seg.
Participe 225 vezes mais rápido
Conclusão: evite WHERE para especificar relações e use JOIN sempre que possível (definitivamente em LINQ to DataSet e Linq-To-Objects
em geral).
fonte
Join
custa nada usar , por que confiar em um otimizador se você pode escrever o código otimizado desde o início? Também torna suas intenções mais claras. Portanto, as mesmas razões pelas quais você deve preferir JOIN no sql .Respostas:
Sua primeira abordagem (consulta SQL no banco de dados) é bastante eficiente porque o banco de dados sabe como realizar uma junção. Mas realmente não faz sentido compará-lo com as outras abordagens, uma vez que funcionam diretamente na memória (Linq para DataSet)
A consulta com várias tabelas e uma
Where
condição realmente executa um produto cartesiano de todas as tabelas e, em seguida , filtra as linhas que satisfazem a condição. Isso significa que aWhere
condição é avaliada para cada combinação de linhas (n1 * n2 * n3 * n4)O
Join
operador obtém as linhas das primeiras tabelas, em seguida, obtém apenas as linhas com uma chave correspondente da segunda tabela e, a seguir, apenas as linhas com uma chave correspondente da terceira tabela e assim por diante. Isso é muito mais eficiente, porque não precisa realizar tantas operaçõesfonte
where
-query de alguma forma, assim como um dbms. Na verdade, oJOIN
foi até 225 vezes mais rápido que oWHERE
(última edição).o
Join
é muito mais rápido, pois o método sabe combinar as tabelas para reduzir o resultado às combinações relevantes. Quando você usaWhere
para especificar a relação, é necessário criar todas as combinações possíveis e, em seguida, testar a condição para ver quais combinações são relevantes.O
Join
método pode configurar uma tabela hash para usar como um índice para compactar rapidamente duas tabelas, enquanto oWhere
método é executado depois que todas as combinações já foram criadas, então ele não pode usar nenhum truque para reduzir as combinações de antemão.fonte
join
palavra - chave, pois não há análise de tempo de execução da consulta para produzir algo análogo a um plano de execução. Você também notará que as junções baseadas em LINQ só podem acomodar equijoins de coluna única.... on new { f1.Key1, f1.Key2 } equals new { f2.Key1, f2.Key2 }
o que você realmente precisa saber é o sql que foi criado para as duas instruções. Existem algumas maneiras de fazer isso, mas a mais simples é usar o LinqPad. Existem vários botões logo acima dos resultados da consulta que serão alterados para sql. Isso lhe dará muito mais informações do que qualquer outra coisa.
Ótima informação que você compartilhou lá.
fonte