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

时间:2011-04-05 11:45:20

标签: .net linq performance join linq-to-dataset

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

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

我检查了3种方法:

  1. 直接数据库
  2. LINQ查询其中条件为“加入” - 语法
  3. LINQ查询加入 - 语法
  4. 这些是每个函数1000次调用的结果:

    1.Iteration:

    1. 4,2841519 sec。
    2. 115,7796925 sec。
    3. 2,024749秒。
    4. 2.Iteration:

      1. 3,1954857 sec。
      2. 84,97047 sec。
      3. 1,5783397秒。
      4. 3.Iteration:

        1. 2,7922143秒。
        2. 97,8713267秒。
        3. 1,8432163 sec。
        4. 平均:

          1. 数据库:3,4239506333秒。
          2. 其中:99,5404964秒。
          3. 加入:1,815435秒
          4. 为什么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
            

            加入:

            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
            

            提前谢谢。


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

            其中

            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
            

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

            1. 加入| 2.哪里
            2. 1.Iteration:

              1. 0,0713669 sec。
              2. 12,7395299秒。
              3. 2.Iteration:

                1. 0,0492458 sec。
                2. 12,3885925 sec。
                3. 3.Iteration:

                  1. 0,0501982 sec。
                  2. 13,3474216 sec。
                  3. 平均:

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

                    3. 加入速度快了225倍

                      结论:避免WHERE指定关系并尽可能使用JOIN(通常在LINQ to DataSetLinq-To-Objects中)。

3 个答案:

答案 0 :(得分:74)

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

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

  3. Join运算符从第一个表中获取行,然后仅从第二个表中获取具有匹配键的行,然后仅获取第三个表中具有匹配键的行,等等上。这样效率更高,因为它不需要执行任何操作

答案 1 :(得分:19)

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

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

答案 2 :(得分:7)

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

你在那里分享了很棒的信息。