LEFT JOIN 또는 NOT EXISTS 사용 모범 사례


67

LEFT JOIN 또는 NOT EXISTS 형식을 사용하는 것이 가장 좋은 방법입니까?

다른 것을 사용하면 어떤 이점이 있습니까?

없는 경우 어떤 것을 선호해야합니까?

SELECT *
FROM tableA A
LEFT JOIN tableB B
     ON A.idx = B.idx
WHERE B.idx IS NULL

SELECT *
FROM tableA A
WHERE NOT EXISTS
(SELECT idx FROM tableB B WHERE B.idx = A.idx)

SQL Server 데이터베이스에 대해 Access 내에서 쿼리를 사용하고 있습니다.


2
여담으로, 겉으로는-동일한 접근 방식 WHERE A.idx NOT IN (...) 입니다 하지 동일 인해의 3가 행동 NULL(즉, NULL동일하지 않습니다 NULL당신이 때문에 경우) (도 불평등 한 어떤 NULL 에서 tableB예기치 않은 결과를 얻을 수 있습니다!)
Elaskanator

답변:


58

가장 큰 차이점은 조인에 존재하지 않는 것과 존재하지 않는 것입니다 SELECT *.

첫 번째 예에서, 당신은에서 모든 열을 얻을 모두 AB두 번째 예를 들어, 당신은 만 열을 얻을 수있는 반면, A.

SQL Server에서 두 번째 변형은 매우 간단한 구성 예에서 약간 더 빠릅니다.

두 개의 샘플 테이블을 작성하십시오.

CREATE TABLE dbo.A
(
    A_ID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
);

CREATE TABLE dbo.B
(
    B_ID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
);
GO

각 테이블에 10,000 개의 행을 삽입하십시오.

INSERT INTO dbo.A DEFAULT VALUES;
GO 10000

INSERT INTO dbo.B DEFAULT VALUES;
GO 10000

두 번째 테이블에서 5 번째 행을 모두 제거하십시오.

DELETE 
FROM dbo.B 
WHERE B_ID % 5 = 1;

SELECT COUNT(*) -- shows 10,000
FROM dbo.A;

SELECT COUNT(*) -- shows  8,000
FROM dbo.B;

두 가지 테스트 SELECT문 변형을 수행하십시오 .

SELECT *
FROM dbo.A
    LEFT JOIN dbo.B ON A.A_ID = B.B_ID
WHERE B.B_ID IS NULL;

SELECT *
FROM dbo.A
WHERE NOT EXISTS (SELECT 1
    FROM dbo.B
    WHERE b.B_ID = a.A_ID);

실행 계획 :

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

두 번째 변형은 왼쪽 반 세미 결합 연산자를 사용할 수 있으므로 필터 작업을 수행 할 필요가 없습니다.


23

논리적으로 그것들은 동일하지만 NOT EXISTS당신이 요구하는 AntiSemiJoin에 더 가깝고 일반적으로 선호됩니다. 또한 B의 열에 액세스 할 수 없다는 것이 더 중요합니다 .Bull의 열에 액세스 할 수있는 것과는 대조적으로 필터로만 사용되기 때문입니다.

몇 년 전 (SQL Server 6.0 ish) LEFT JOIN은 더 빠르지 만 오래전부터 그렇지 않았습니다. 요즘 NOT EXISTS은 조금 더 빠릅니다.


Access에서 가장 큰 영향은 JOIN메서드가 조인을 필터링하기 전에 조인을 완료하여 메모리에 조인 된 집합을 구성해야한다는 것입니다. NOT EXISTS그것을 사용 하면 행을 확인하지만 열을위한 공간을 할당하지 않습니다. 또한 행을 찾으면 찾기가 중지됩니다. Access에서는 성능이 약간 더 다양하지만 일반적으로 경험 NOT EXISTS하면 약간 더 빠릅니다. 더 많은 요소가 관련되어 있기 때문에 "모범 사례"라고 말하는 경향이 적습니다.


6

Linked Servers를 사용할 때 NOT EXISTS(그러나 조금씩) 우수하다는 것을 알았 습니다 .LEFT JOIN ... WHERE IS NULL

실행 계획을 검토하면 NOT EXISTS연산자가 중첩 루프 방식으로 실행되는 것으로 보입니다 . 그렇게하면 행 단위로 실행됩니다 (필자가 생각합니다).

이 동작을 보여주는 실행 계획 예 : 여기에 이미지 설명을 입력하십시오


1
연결된 서버는 그런 종류의 일에 잔인합니다. 이 문제를 해결하기위한 가능한 방법은 링크 된 서버 링크를 통해 원격 데이터를 복사 INSERT INTO #t (a,b,c) SELECT a,b,c FROM LinkedServer.database.dbo.table WHERE x=y한 다음 NOT EXISTS (...)해당 데이터베이스의 임시 사본에 대해 절 을 실행하는 것 입니다.
Max Vernon

2
Max Vernon에서 내 게시물에 대한 답변을 얻으려면 지금 조금 부끄럽습니다! 팬 보이는 옆으로 서버 간 상황을 최대한 활용하기 위해 여러 번의 정확한 접근 방식을 사용했기 때문에 재미있는 이야기입니다.
robopim

1
건배, @pimbrouwers-당신의 친절한 의견에 감사드립니다!
Max Vernon

5

일반적으로 엔진은 기본적으로 다음을 기반으로 실행 계획을 만듭니다.

  1. A와 B의 행 수
  2. A 및 / 또는 B에 색인이 있는지 여부
  3. 예상되는 결과 행 수 (및 중간 행)
  4. 입력 쿼리 형식 (예 : 질문)

(4)의 경우 :

"존재하지 않음"계획은 테이블 B에 대한 탐색 기반 계획을 권장합니다. 이는 테이블 A가 작고 테이블 B가 클 때 (및 인덱스가 B에있는 경우) 좋은 선택입니다.

"antijoin"계획은 테이블 A가 매우 크거나 테이블 B가 매우 작거나 B에 대한 인덱스가없고 큰 결과 세트를 리턴 할 때 좋은 선택입니다.

그러나 이것은 가중 입력과 같은 "권장"입니다. 강한 (1), (2), (3)은 종종 (4) ot을 선택합니다.

(*로 인해 다른 열을 반환하는 예제의 효과를 무시하면 @MaxVernon 답변으로 해결됩니다.).

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