为什么LINQ JOIN比使用WHERE链接要快得多?

Tim*_*ter 96 .net linq performance join linq-to-dataset

我最近升级到了VS 2010,正在玩LINQ to Dataset.我有一个强大的类型化的Authorization数据集,它位于ASP.NET WebApplication的HttpCache中.

所以我想知道实际上检查用户是否有权做某事的最快方法.如果有人感兴趣的话,是我的数据模型和其他一些信息.

我检查了3种方法:

  1. 直接数据库
  2. LINQ查询的Where条件为"Join" - 语法
  3. 带有Join的 LINQ查询- 语法

这些是每个函数1000次调用的结果:

1.Iteration:

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

2.Iteration:

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

3.Iteration:

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

平均:

  1. 数据库:3,4239506333秒.
  2. 其中:99,5404964秒.
  3. 加入:1,815435秒.

为什么Join-version比where-syntax快得多,这使得它无用尽管作为LINQ新手它似乎是最清晰的.或者我在查询中遗漏了什么?

这是LINQ查询,我跳过数据库:

地点:

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
Run Code Online (Sandbox Code Playgroud)

加入:

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
Run Code Online (Sandbox Code Playgroud)

先感谢您.


编辑:在对两个查询进行一些改进以获得更有意义的性能值之后,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
Run Code Online (Sandbox Code Playgroud)

地点:

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
Run Code Online (Sandbox Code Playgroud)

1000次通话的结果(在更快的计算机上)

  1. 加入| 2.哪里

1.Iteration:

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

2.Iteration:

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

3.Iteration:

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

平均:

  1. 加入:0,0569367秒.
  2. 其中:12,8251813秒.

加入速度提高了225倍

结论:避免在指定的关系,并使用JOIN尽可能(肯定是在LINQ到数据集Linq-To-Objects一般).

Tho*_*que 75

  1. 您的第一种方法(DB中的SQL查询)非常有效,因为DB知道如何执行连接.但是将它与其他方法进行比较并没有多大意义,因为它们直接在内存中工作(Linq to DataSet)

  2. 具有多个表和Where条件的查询实际上执行所有表的笛卡尔积,然后过滤满足条件的行.这意味着Where为每个行组合评估条件(n1*n2*n3*n4)

  3. Join操作者需要从所述第一表中的行,然后与从第二表中的匹配项,则仅利用从所述第三表中的匹配键的行中,等等只需要的行.这样效率更高,因为它不需要执行许多操作

  • 感谢您澄清背景.db方法实际上不是这个问题的一部分,但我很有兴趣看看内存方法是否真的更快.我假设.net会以某种方式优化`where`-query作为dbms.实际上`JOIN`甚至比`WHERE`(最后编辑)快225倍. (4认同)

Guf*_*ffa 19

Join要快得多,因为方法知道如何将表结合起来,以减少结果的相关组合.当您用于Where指定关系时,它必须创建每个可能的组合,然后测试条件以查看哪些组合是相关的.

Join方法可以设置哈希表以用作快速压缩两个表的索引,而该Where方法在已经创建所有组合之后运行,因此它不能使用任何技巧来预先减少组合.

  • @Tim Schelter,当您使用Linq to SQL或Linq to Entities时会有这样的优化,因为生成的SQL查询被DBMS视为连接.但是在这种情况下你使用Linq到DataSet,没有SQL转换 (6认同)
  • @Adam,这不完全正确:你可以使用匿名类型对多个键进行equijoins:`... on new {f1.Key1,f1.Key2}等于new {f2.Key1,f2.Key2}` (2认同)

phi*_*lip 7

你真正需要知道的是为这两个语句创建的sql.有几种方法可以实现,但最简单的方法是使用LinqPad.查询结果正上方有几个按钮将更改为sql.这将为您提供比其他任何东西更多的信息.

你分享的很棒的信息.