Почему LINQ JOIN намного быстрее, чем связь с WHERE? - PullRequest
96 голосов
/ 05 апреля 2011

Я недавно обновился до VS 2010 и играю с LINQ to Dataset. У меня есть строго типизированный набор данных для авторизации, который находится в HttpCache веб-приложения ASP.NET.

Итак, я хотел знать, что на самом деле является самым быстрым способом проверить, авторизован ли пользователь на что-либо. Здесь - моя модель данных и некоторые другие сведения, если кому-то это интересно.

Я проверил 3 способа:

  1. прямой база данных
  2. LINQ-запрос с , где означает «Join» - синтаксис
  3. 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. Регистрация: 1815435 сек.

Почему Join-версия намного быстрее, чем синтаксис where, что делает ее бесполезной, хотя как новичок в 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

Заранее спасибо.


Редактировать : после некоторых улучшений в обоих запросах, чтобы получить более значимые значения perfomance, преимущество 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. Где

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 to DataSet и Linq-To-Objects в целом).

Ответы [ 3 ]

75 голосов
/ 05 апреля 2011
  1. Ваш первый подход (SQL-запрос в БД) достаточно эффективен, потому что БД знает, как выполнить соединение. Но на самом деле не имеет смысла сравнивать его с другими подходами, поскольку они работают непосредственно в памяти (Linq to DataSet)

  2. Запрос с несколькими таблицами и условием Where фактически выполняет декартово произведение всех таблиц, , а затем фильтрует строки, удовлетворяющие условию. Это означает, что условие Where оценивается для каждой комбинации строк (n1 * n2 * n3 * n4)

  3. Оператор Join берет строки из первых таблиц, затем берет только строки с совпадающим ключом из второй таблицы, затем только строки с совпадающим ключом из третьей таблицы и так далее. Это гораздо эффективнее, потому что не нужно выполнять столько операций

19 голосов
/ 05 апреля 2011

Join намного быстрее, потому что метод знает, как объединить таблицы, чтобы свести результат к соответствующим комбинациям.Когда вы используете Where для указания отношения, он должен создать каждую возможную комбинацию, а затем проверить условие, чтобы увидеть, какие комбинации релевантны.

Метод Join может настроить хеш-таблицу для использованияв качестве индекса для быстрого объединения двух таблиц, в то время как метод Where выполняется после того, как все комбинации уже созданы, поэтому он не может использовать какие-либо приемы для предварительного сокращения комбинаций.

7 голосов
/ 05 апреля 2011

что вам действительно нужно знать, это sql, который был создан для двух операторов.Есть несколько способов добраться до него, но самым простым является использование LinqPad.Прямо над результатами запроса есть несколько кнопок, которые изменятся на sql.Это даст вам гораздо больше информации, чем что-либо еще.

Отличная информация, которой вы поделились там.

...