SQL Server에 종속 외부 조인을 중첩시켜야합니까?


9

나는 이것에 대한 혼합 정보를 듣고 정식 또는 전문가 의견을 기대하고 있습니다.

LEFT OUTER JOIN각각에 마지막에 의존하는 여러 개가있는 경우 중첩하는 것이 더 낫습니까?

고안된 예를 들어, JOINto MyParentJOINto MyChild: 에 의존 합니다 : http://sqlfiddle.com/#!3/31022/5

SELECT
    {columns}
FROM
    MyGrandChild AS gc
LEFT OUTER JOIN
    MyChild AS c
        ON c.[Id] = gc.[ParentId]
LEFT OUTER JOIN
    MyParent AS p
        ON p.[id] = c.[ParentId]

여기에 이미지 설명을 입력하십시오

http://sqlfiddle.com/#!3/31022/7과 비교

SELECT
    {columns}
FROM
    MyGrandChild AS gc
LEFT OUTER JOIN
    (
    MyChild AS c            
    LEFT OUTER JOIN
        MyParent AS p
            ON p.[id] = c.[ParentId]
    )
    ON c.[Id] = gc.[ParentId]

여기에 이미지 설명을 입력하십시오

위에 표시된 것처럼 SS2k8에서 다른 쿼리 계획을 생성합니다.


중첩 조인을 사용 하는 것이 좋습니다. michaeljswart.com/2012/09/when-i-use-nested-joins 스타일 문제 일 수 있습니다.
Michael J Swart

@MichaelJS 상담원이 다음과 같은 경우에 귀하의 블로그 만 토론하는 것으로 보입니다 JOIN.INNER JOIN
Matthew

1
"더 나은"을 어떻게 정의 하시겠습니까? 개인적으로, 나는 첫 번째가 훨씬 더 읽기 쉽다는 것을 알았습니다. 관계를 리버스 엔지니어링하려고한다고 생각하지 않습니다. ON ... ON연속으로 두 번 (괄호 안 또는 안) 을 갖는 것은 매우 혼란 스럽습니다.
Aaron Bertrand

4
무언가를하는 두 가지 방법 사이에 성능 차이가 없으면 다음 질문은 버스에 부딪 히거나 오늘 밤 복권에 당첨되면 내일 코드를 인계받는 사람이 가장 쉽게 이해하고 유지할 수있는 버전입니다. ?
Aaron Bertrand

1
use plan하지만 그 반대의 경우도 마찬가지 첫 번째와 두 번째 쿼리 계획을 이식 할 때 힌트가 작동합니다.
Martin Smith

답변:


3

이것은 정식 답변은 아니지만 SQL Fiddle에 표시된 중첩 루프 쿼리 계획의 경우 USE PLAN힌트를 사용하여 쿼리 2에서 쿼리 1로 계획을 적용 할 수 있지만 역방향 작업을 시도하면 실패합니다.

USE PLAN 힌트에 쿼리에 적합한 것으로 검증 될 수없는 계획이 포함되어 있으므로 쿼리 프로세서가 쿼리 계획을 생성 할 수 없습니다. USE PLAN 힌트를 제거하거나 교체하십시오. 성공적인 계획 강제 실행 가능성을 높이려면 USE PLAN 힌트에 제공된 계획이 동일한 쿼리에 대해 SQL Server에서 자동으로 생성 된 계획인지 확인하십시오.

옵티 마이저 변환 규칙을 사용 안함으로 설정하면 ReorderLOJN 이전에 성공한 계획 힌트도 성공하지 못합니다.

데이터 쇼의 큰 양으로 실험하는 것은 SQL Server는 변화의 확실 할 수 있는지 (A LOJ B) LOJ CA LOJ (B LOJ C)자연스럽게뿐만 아니라,하지만 난 반대가 사실임을 증거를 보지 않았다.

첫 번째 쿼리가 두 번째 쿼리보다 더 잘 수행되는 매우 고안된 사례는 다음과 같습니다.

DROP TABLE  MyGrandChild , MyChild,  MyParent

CREATE TABLE MyParent
(Id int)

CREATE TABLE MyChild
(Id int PRIMARY KEY
,ParentId int,
Filler char(8000) NULL)

CREATE TABLE MyGrandChild
(Id int
,ParentId int)

INSERT INTO MyChild
                      (Id, ParentId)
SELECT TOP (100000) ROW_NUMBER() OVER (ORDER BY @@SPID),
                     ROW_NUMBER() OVER (ORDER BY @@SPID)    
FROM master..spt_values  v1, master..spt_values                  

INSERT INTO MyGrandChild
                      (Id, ParentId)
OUTPUT INSERTED.Id INTO MyParent
SELECT TOP (3000) Id, Id AS ParentId
FROM MyChild
ORDER BY Id

SET STATISTICS IO ON;
SET STATISTICS TIME ON;

SELECT gc.Id       AS gcId,
       gc.ParentId AS gcpId,
       c.Id        AS cId,
       c.ParentId  AS cpId,
       p.Id        AS pId
FROM   MyGrandChild AS gc
       LEFT OUTER JOIN MyChild AS c
         ON c.[Id] = gc.[ParentId]
       LEFT OUTER JOIN MyParent AS p
         ON p.[Id] = c.[ParentId]

SELECT gc.Id       AS gcId,
       gc.ParentId AS gcpId,
       c.Id        AS cId,
       c.ParentId  AS cpId,
       p.Id        AS pId
FROM   MyGrandChild AS gc
       LEFT OUTER JOIN( MyChild AS c
                        LEFT OUTER JOIN MyParent AS p
                          ON p.[Id] = c.[ParentId])
         ON c.[Id] = gc.[ParentId] 

어느 계획을 제공

여기에 이미지 설명을 입력하십시오

나를 위해 쿼리 1의 경과 시간은 108ms 대 쿼리 2의 경우 1,163ms였습니다.

쿼리 1

Table 'Worktable'. Scan count 0, logical reads 0 
Table 'MyChild'. Scan count 0, logical reads 9196
Table 'MyGrandChild'. Scan count 1, logical reads 7
Table 'MyParent'. Scan count 1, logical reads 5

쿼리 2

Table 'MyParent'. Scan count 1, logical reads 15000
Table 'MyChild'. Scan count 0, logical reads 9000 
Table 'MyGrandChild'. Scan count 1, logical reads 7

따라서 첫 번째 ( "unested") 구문은 잠재적 인 조인 순서를 더 많이 고려할 수 있기 때문에 잠재적으로 유리할 수 있지만 잠정적으로 충분한 테스트를 수행하지 않아서 일반적인 규칙으로이를 확신 할 수는 없습니다.

쿼리 2가 더 잘 수행되는 카운터 예제를 만드는 것이 가능할 수도 있습니다. 둘 다 시도하고 실행 계획을 살펴보십시오.


-1

"Nested Join"이라는 JOIN 유형이 없습니다. 가독성 편의를 위해 JOIN을 작성하는 또 다른 변형입니다. 이해 목적으로 만 "하위 쿼리"로 볼 수 있습니다.

코드 가독성에 대해 더 관심이 있으시다면 제가 취할 수있는 것은 개인이 선택하는 것입니다.

쿼리 성능에 문제가 있고 쿼리에 "강제 조인"힌트가 사용되지 않으면 쿼리가 "Nested Join"또는 All "Outer Join"으로 작성된 경우에는 문제가되지 않습니다. SQL Server는 두 테이블 및 / 또는 결과를 조인하는 비용을 기준으로 순서를 정합니다. SQL Server는 한 번에 두 데이터 집합간에 만 참여합니다.

실제로, SQL 서버가 두 번째 부분 인 "MyChild AS c LEFT OUTER JOIN MyParent AS p ON p. [id] = c. [ParentId]"를 수행하기로 결정한 경우 두 번째 방식으로 "중첩 조인"을 가정하고 해당 테이블이 발생한다고 가정하십시오. NEXT LEFT JOIN에서 버릴 행이 있어야합니다. 이 경우 SQL Server는 외부 조인을 수행하고 그 결과를 다음 조인으로 전달하는 데 불필요한 리소스를 소비했습니다.

비슷한 질문을하고 적절하게 대답 할 수도 있습니다. 'Nested join'구문 이해


1
그렇다면 왜 FORCE JOIN ORDER힌트 를 사용하지 않고 다른 쿼리 계획을 생성 합니까?
Matthew

힌트가 없으면 JOIN 명령을 보장 할 수 없으며 다른 실행 계획을 보았을 때이를 증명합니다. 예를 들어 "모든 외부 조인 사용"SQL 서버는이 두 가지 중 하나를 수행 할 수 있습니다. 먼저 "MyChild + MyGrandChild"를 입력 한 다음 "MyParent"에 가입하십시오. 또는 먼저 "MyChild + MyParent"를 입력 한 다음 "MyGrandChild"에 가입하십시오.
Anup Shah
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.