CROSS APPLY는 외부 조인을 생성합니다


17

파티션에 비해 별개의 SQL 계산에 대한 답으로 Erik Darling은이 코드를 게시하여 다음과 같은 부족한 부분을 해결했습니다 COUNT(DISTINCT) OVER ().

SELECT      *
FROM        #MyTable AS mt
CROSS APPLY (   SELECT COUNT(DISTINCT mt2.Col_B) AS dc
                FROM   #MyTable AS mt2
                WHERE  mt2.Col_A = mt.Col_A
                -- GROUP BY mt2.Col_A 
            ) AS ca;

쿼리가 CROSS APPLY(아님 OUTER APPLY)를 사용하므로 실행 계획에 내부 조인 대신 외부 조인 이있는 이유는 무엇입니까?

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

또한 group by 절을 주석 해제하면 왜 내부 조인이 발생합니까?

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

나는 데이터가 중요하지 않다고 생각하지만 다른 질문에서 kevinwhat이 제공 한 데이터를 복사합니다.

create table #MyTable (
Col_A varchar(5),
Col_B int
)

insert into #MyTable values ('A',1)
insert into #MyTable values ('A',1)
insert into #MyTable values ('A',2)
insert into #MyTable values ('A',2)
insert into #MyTable values ('A',2)
insert into #MyTable values ('A',3)

insert into #MyTable values ('B',4)
insert into #MyTable values ('B',4)
insert into #MyTable values ('B',5)

답변:


23

요약

SQL Server는 올바른 조인 (내부 또는 외부)을 사용하고 applyjoin 사이의 내부 변환을 수행 할 때 원래 쿼리의 모든 의미준수 하기 위해 필요한 경우 투영을 추가합니다 .

계획의 차이점 은 SQL Server에서 group by 절이 있거나없는 집계 의 서로 다른 의미 로 설명 할 수 있습니다 .


세부

가입 대 신청

applyjoin 을 구별 할 수 있어야합니다 .

  • 대다

    Apply 의 내부 (아래) 입력은 외부 (위) 입력의 각 행에 대해 실행되며 현재 외부 행에서 제공하는 하나 이상의 내부 매개 변수 값이 있습니다. 적용 의 전체 결과 는 매개 변수화 된 내부 실행으로 생성 된 모든 행의 조합 (모두 조합)입니다. 매개 변수가 존재한다는 것은 적용 은 때때로 상관 조인이라고합니다.

    적용 항상 의한 실행 계획에 구현되어 중첩 루프 연산자. 연산자는 결합 술어가 아닌 외부 참조 특성을 갖습니다 . 외부 참조는 루프의 각 반복에서 외부에서 내부로 전달되는 매개 변수입니다.

  • 붙다

    결합은 결합 연산자에서 결합 술어를 평가합니다. 조인은 일반적으로 SQL Server의 Hash Match , Merge 또는 Nested Loops 연산자 로 구현할 수 있습니다 .

    중첩 루프 를 선택 하면 외부 참조 가 부족 하고 일반적으로 결합 술어가 존재 하므로 적용 과 구별 될 수 있습니다 . 조인 의 내부 입력은 외부 입력의 값을 절대 참조하지 않습니다. 내부는 여전히 각 외부 행에 대해 한 번 실행되지만 내부 실행은 현재 외부 행의 값에 의존하지 않습니다.

자세한 내용은 내 게시물 Apply vs Nested Loops Join을 참조하십시오 .

... 내부 조인 대신 실행 계획에 외부 조인 이있는 이유는 무엇입니까?

외부 조인은 옵티마이 저가보다 저렴한 조인 기반 계획을 찾을 수 있는지 확인 하기 위해 조인에 적용 (이라는 규칙 사용 )을 변환 할 때 발생합니다 . 적용스칼라 집계 가 포함 된 경우 조인은 정확성을 위해 외부 조인이어야합니다 . 내부 조인은 원래 적용 할 때와 동일한 결과를 보장 하지 않습니다 .ApplyHandler

스칼라 및 벡터 집계

  • 해당 GROUP BY절이 없는 집계 는 스칼라 집계입니다.
  • 해당 GROUP BY절이 있는 집계 는 벡터 집계입니다.

SQL Server에서 스칼라 집계는 집계 할 행이없는 경우에도 항상 행을 생성합니다. 예를 들어, COUNT행이없는 스칼라 집계는 0입니다. 벡터 COUNT 없는 행 집합은 공집합 (전혀 행)입니다.

다음 장난감 쿼리는 차이점을 보여줍니다. 내 기사 Scalar 및 Vector Aggregates의 Fun 에서 스칼라 및 벡터 집계에 대한 자세한 내용을 읽을 수도 있습니다 .

-- Produces a single zero value
SELECT COUNT_BIG(*) FROM #MyTable AS MT WHERE 0 = 1;

-- Produces no rows
SELECT COUNT_BIG(*) FROM #MyTable AS MT WHERE 0 = 1 GROUP BY ();

db <> 바이올린 데모

변형이 조인에 적용됩니다.

이전 에 원래 적용스칼라 집계 가 포함 된 경우 조인이 정확성을 위해 외부 조인이어야한다고 언급했습니다 . 이것이 왜 그런지 자세히 보여주기 위해, 간단한 질문 질의 예제를 사용할 것입니다.

DECLARE @A table (A integer NULL, B integer NULL);
DECLARE @B table (A integer NULL, B integer NULL);

INSERT @A (A, B) VALUES (1, 1);
INSERT @B (A, B) VALUES (2, 2);

SELECT * FROM @A AS A
CROSS APPLY (SELECT c = COUNT_BIG(*) FROM @B AS B WHERE B.A = A.A) AS CA;

스칼라 집계 이므로 column c에 대한 올바른 결과 는 0 입니다. 이 적용 쿼리를 조인 형식으로 변환 할 때 SQL Server는 T-SQL로 표현 된 경우 다음과 유사한 내부 대안을 생성합니다.COUNT_BIG

SELECT A.*, c = COALESCE(J1.c, 0)
FROM @A AS A
LEFT JOIN
(
    SELECT B.A, c = COUNT_BIG(*) 
    FROM @B AS B
    GROUP BY B.A
) AS J1
    ON J1.A = A.A;

적용을 비 상관 조인으로 다시 쓰려면 GROUP BY파생 테이블에 를 도입해야 합니다 (그렇지 않으면 A조인 할 열 이 없을 수 있음 ). 조인은 외부 조인 이어야 하므로 테이블의 각 행이 @A계속 출력에서 ​​행을 생성합니다. 결합 술어가 true로 평가되지 않으면 왼쪽 결합은 NULLfor 열 을 생성 c합니다. 그건 NULL필요에 의해 제로로 번역 할 수 COALESCE에서 정확한 변환 완료 적용을 .

아래 데모는 외부 조인과 원래 적용 쿼리 COALESCE와 같은 조인 을 사용하여 동일한 결과를 생성하는 방법을 보여줍니다 .

db <> 바이올린 데모

이랑 GROUP BY

... 그룹 별 절을 주석 해제하면 왜 내부 조인이 발생합니까?

단순화 된 예제를 계속하면서 다음을 추가하십시오 GROUP BY.

DECLARE @A table (A integer NULL, B integer NULL);
DECLARE @B table (A integer NULL, B integer NULL);

INSERT @A (A, B) VALUES (1, 1);
INSERT @B (A, B) VALUES (2, 2);

-- Original
SELECT * FROM @A AS A
CROSS APPLY 
(SELECT c = COUNT_BIG(*) FROM @B AS B WHERE B.A = A.A GROUP BY B.A) AS CA;

COUNT_BIG지금이다 벡터 빈 입력 세트에 대한 올바른 결과가 더 이상 제로 없도록는 없다, 집계 전혀 행 . 즉, 위의 명령문을 실행하면 출력이 생성되지 않습니다.

이 의미는 apply 에서 join으로 변환 할 때 CROSS APPLY내부 행을 생성하지 않는 외부 행을 자연스럽게 거부 하므로 적용 하기 가 훨씬 쉽습니다 . 따라서 추가적인 표현식 투영없이 내부 조인을 안전하게 사용할 수 있습니다.

-- Rewrite
SELECT A.*, J1.c 
FROM @A AS A
JOIN
(
    SELECT B.A, c = COUNT_BIG(*) 
    FROM @B AS B
    GROUP BY B.A
) AS J1
    ON J1.A = A.A;

아래 데모는 내부 조인 재 작성이 벡터 집계를 사용한 원래 적용과 동일한 결과를 생성 함을 보여줍니다.

db <> 바이올린 데모

옵티마이 저는 저렴한 조인 계획 (빠른 계획이 충분 함)을 빨리 찾기 때문에 작은 테이블과의 병합 내부 조인을 선택합니다 . 비용 기반 옵티마이 저는 루프 조인 또는 ForceSeek 힌트를 사용하는 경우 여기에서와 같이 저렴하게 적용 계획을 찾는 대신 적용에 다시 조인을 다시 작성하지만이 경우에는 노력할 가치가 없습니다.

노트

단순화 된 예는 의미가 다른 것을보다 명확하게 보여주기 위해 내용이 다른 여러 테이블을 사용합니다.

옵티마이 저는 자체 조인이 일치하지 않는 (조인하지 않는) 행을 생성 할 수 없다는 이유로 추론 할 수 있어야한다고 주장 할 수 있지만 오늘날에는 해당 논리가 포함되어 있지 않습니다. 쿼리에서 동일한 테이블에 여러 번 액세스해도 격리 수준 및 동시 활동에 따라 동일한 결과가 생성되는 것은 아닙니다.

옵티마이 저는 이러한 의미 및 엣지 케이스에 대해 걱정하므로 필요하지 않습니다.


보너스 : 내부 적용 플랜

SQL Server 예제 쿼리에 대해 내부 적용 계획 (내부 조인 계획이 아님 !)을 생성 수 있으며 비용 때문에하지 않기로 선택합니다. 질문에 표시된 외부 조인 계획의 비용은 랩톱의 SQL Server 2017 인스턴스에서 0.02898 단위입니다.

설명을 위해 문서화되지 않은 지원되지 않는 추적 플래그 9114 (비활성화 등) 를 사용하여 적용 (상관 결합) 계획을 강제 실행할 수 있습니다 ApplyHandler.

SELECT      *
FROM        #MyTable AS mt
CROSS APPLY 
(
    SELECT COUNT_BIG(DISTINCT mt2.Col_B) AS dc
    FROM   #MyTable AS mt2
    WHERE  mt2.Col_A = mt.Col_A 
    --GROUP BY mt2.Col_A
) AS ca
OPTION (QUERYTRACEON 9114);

지연 인덱스 스풀이있는 중첩 루프 적용 계획 이 생성 됩니다 . 총 예상 비용은 0.0463983입니다 (선택한 계획보다 높음).

인덱스 스풀 적용 계획

Apply 중첩 루프를 사용하는 실행 계획 은 GROUP BY절의 존재 여부에 관계없이 "내부 조인"시맨틱을 사용하여 올바른 결과를 생성합니다 .

현실 세계에서, 우리는 일반적으로는의가 안쪽에 찾아 지원하는 인덱스 것이다 적용 예를 들어, 자연이 옵션을 선택하는 SQL 서버를 격려하기를 :

CREATE INDEX i ON #MyTable (Col_A, Col_B);

db <> 바이올린 데모


-3

교차 적용은 데이터에 대한 논리적 작업입니다. 해당 데이터를 얻는 방법을 결정할 때 SQL Server는 원하는 데이터를 얻기 위해 적절한 물리적 연산자를 선택합니다.

물리적 적용 연산자는 없으며 SQL Server는이를 적절하고 희망적으로 효율적인 조인 연산자로 변환합니다.

아래 링크에서 실제 연산자 목록을 찾을 수 있습니다.

https://docs.microsoft.com/en-us/sql/relational-databases/showplan-logical-and-physical-operators-reference?view=sql-server-2017

쿼리 최적화 프로그램은 쿼리 계획을 논리 연산자로 구성된 트리로 만듭니다. 쿼리 최적화 프로그램이 계획을 만든 후 쿼리 최적화 프로그램은 각 논리 연산자에 대해 가장 효율적인 실제 연산자를 선택합니다. 쿼리 최적화 프로그램은 비용 기반 접근 방식을 사용하여 논리 연산자를 구현할 실제 연산자를 결정합니다.

일반적으로 논리적 작업은 여러 물리적 연산자로 구현할 수 있습니다. 그러나 드문 경우이지만 물리 연산자는 여러 논리 연산도 구현할 수 있습니다.

편집 / 귀하의 질문이 잘못 이해 된 것 같습니다. SQL 서버는 일반적으로 가장 적합한 연산자를 선택합니다. 쿼리는 크로스 조인이 사용되는 두 테이블의 모든 조합에 대한 값을 반환 할 필요가 없습니다. 각 행에 대해 원하는 값을 계산하면 여기에서 수행되는 것으로 충분합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.