LINQ JOIN이 WHERE와 연결하는 것보다 훨씬 빠른 이유는 무엇입니까?


99

최근에 VS 2010으로 업그레이드했고 LINQ to Dataset을 가지고 놀았습니다. ASP.NET WebApplication의 HttpCache에있는 권한 부여를위한 강력한 형식의 데이터 집합이 있습니다.

그래서 저는 사용자가 무언가를 할 권한이 있는지 확인하는 가장 빠른 방법이 무엇인지 알고 싶었습니다. 누군가가 관심이 있다면 여기 내 데이터 모델과 다른 정보가 있습니다.

세 가지 방법을 확인했습니다.

  1. 직접 데이터베이스
  2. Where 조건이 "Join"인 LINQ 쿼리 -구문
  3. 조인을 사용한 LINQ 쿼리 -구문

다음은 각 함수에 대해 1000 번 호출 한 결과입니다.

1. 반복 :

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

2. 반복 :

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

3. 반복 :

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

평균:

  1. 데이터베이스 : 3,4239506333 초
  2. Where : 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

붙다:

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. 어디서

1. 반복 :

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

2. 반복 :

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

3. 반복 :

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

평균:

  1. 가입 : 0,0569367 초
  2. Where : 12,8251813 초

가입 속도가 225 배 빠릅니다.

결론 : 관계를 지정하기 위해 WHERE를 피하고 가능할 때마다 JOIN을 사용하십시오 (확실히 LINQ to DataSetLinq-To-Objects일반적으로).


이 글을 읽고 LinqToSQL을 사용하고 있고 모든 WHERE를 JOIN으로 변경하는 것이 좋을 것이라고 생각하는 다른 사람들을 위해 THomas Levesque의 주석에서 "Linq to SQL을 사용할 때 이러한 최적화가 있습니다. Linq to Entities, 생성 된 SQL 쿼리는 DBMS에 의해 조인으로 처리되기 때문입니다.하지만이 경우 Linq to DataSet을 사용하는 경우 SQL 로의 변환이 없습니다. " 즉, linqtosql을 WHERE의 조인으로 변환 할 때 아무 것도 변경하지 마십시오.
JonH

@JonH : 사용하는 것이 나쁘지 않습니다 Join. 왜 처음부터 최적화 된 코드를 작성할 수 있다면 최적화 프로그램에 의존합니까? 그것은 또한 당신의 의도를 더 명확하게합니다. 따라서 sql에서 JOIN을 선호해야하는 동일한 이유가 있습니다 .
Tim Schmelter 2015-07-21

이것이 EntityFramework의 경우가 아니라고 가정하는 것이 맞습니까?
Mafii

답변:


76
  1. 첫 번째 방법 (DB의 SQL 쿼리)은 DB가 조인을 수행하는 방법을 알고 있기 때문에 매우 효율적입니다. 그러나 메모리에서 직접 작동하기 때문에 다른 접근 방식과 비교하는 것은 실제로 의미가 없습니다 (Linq에서 DataSet로).

  2. 여러 테이블과 Where조건 이있는 쿼리는 실제로 모든 테이블 의 데카르트 곱을 수행 한 다음 조건을 충족하는 행 필터링합니다. 즉, Where각 행 조합 (n1 * n2 * n3 * n4)에 대해 조건이 평가됩니다.

  3. Join운영자는 제 테이블에서 매칭 키 행만 등 번째 테이블에서 매칭 키 만 행을 얻어 다음, 제 테이블의 행 걸린다. 많은 작업을 수행 할 필요가 없기 때문에 훨씬 더 효율적입니다.


4
배경을 명확히 해주셔서 감사합니다. db 접근 방식은이 질문의 일부가 아니었지만 메모리 접근 방식이 정말 빠른지 확인하는 것은 흥미로 웠습니다. 나는 .net이 wheredbms와 같은 방식으로 -query를 최적화한다고 가정했습니다 . 실제로는 (마지막 편집) JOIN보다 225 배 더 빠릅니다 WHERE.
Tim Schmelter 2011

19

Join방법은 테이블을 결합하여 결과를 관련 조합으로 줄이는 방법을 알고 있기 때문에 훨씬 빠릅니다. 당신이 사용하는 경우 Where의 관계를 지정하기 위해서는 가능한 모든 조합을 만든 다음 관련있는 조합을 볼 수있는 조건을 테스트 할 수 있습니다.

Join메서드는 두 테이블을 빠르게 압축하는 인덱스로 사용할 해시 테이블을 설정할 수 있으며, Where모든 조합이 이미 생성 된 후에 메서드가 실행되므로 사전에 조합을 줄이는 트릭을 사용할 수 없습니다.


감사합니다. dbms와 같이 컴파일러 / 런타임에서 암시 적 최적화가 없습니까? where-relation이 실제로 조인이라는 것을 보는 것이 불가능해서는 안됩니다.
Tim Schmelter 2011

1
좋은 RDBMS는 실제로 WHERE 조건이 두 개의 UNIQUE 열에 대한 동등성 테스트임을 발견하고이를 JOIN으로 처리해야합니다.
Simon Richter 2011

6
@Tim Schelter 님, Linq to SQL 또는 Linq to Entities를 사용할 때 이러한 최적화가 있습니다. 생성 된 SQL 쿼리는 DBMS에서 조인으로 처리되기 때문입니다. 하지만 DataSet에 Linq에를 사용하는 경우에, SQL에 더 번역이 없습니다
토마스 레베

@Tim : LINQ to DataSets는 실제로 LINQ to Objects를 사용합니다. 결과적으로 실제 조인은 join키워드 로만 캡처 할 수 있습니다 . 실행 계획과 유사한 것을 생성하는 쿼리의 런타임 분석이 없기 때문입니다. 또한 LINQ 기반 조인은 단일 열 동등 조인 만 수용 할 수 있습니다.
Adam Robinson

2
@ 아담은 정확하게 진실하지 않는 것이 : 당신은 익명 형식을 사용하여 여러 개의 키와 결 합을 수행 할 수 있습니다... on new { f1.Key1, f1.Key2 } equals new { f2.Key1, f2.Key2 }
토마스 레베

7

당신이 정말로 알아야 할 것은 두 문장을 위해 만들어진 SQL이다. 몇 가지 방법이 있지만 가장 간단한 방법은 LinqPad를 사용하는 것입니다. 쿼리 결과 바로 위에 SQL로 변경되는 몇 가지 버튼이 있습니다. 그것은 당신에게 다른 무엇보다 더 많은 정보를 줄 것입니다.

그래도 훌륭한 정보를 공유했습니다.


1
LinqPad- 힌트에 감사드립니다. 실제로 내 두 쿼리는 메모리 쿼리의 데이터 세트에 대한 linQ이므로 생성 된 SQL이 없다고 가정합니다. 일반적으로 dbms에 의해 최적화됩니다.
Tim Schmelter 2011
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.