[sql] 존재하지 않음 vs 존재하지 않음

이 중 어떤 쿼리가 더 빠릅니까?

존재하지 않음 :

SELECT ProductID, ProductName
FROM Northwind..Products p
WHERE NOT EXISTS (
    SELECT 1
    FROM Northwind..[Order Details] od
    WHERE p.ProductId = od.ProductId)

또는 안 :

SELECT ProductID, ProductName
FROM Northwind..Products p
WHERE p.ProductID NOT IN (
    SELECT ProductID
    FROM Northwind..[Order Details])

쿼리 실행 계획에 따르면 둘 다 동일한 작업을 수행합니다. 이 경우 권장되는 양식은 무엇입니까?

이것은 NorthWind 데이터베이스를 기반으로합니다.

[편집하다]

이 유용한 기사를 찾았습니다.
http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx

나는 존재하지 않을 것이라고 생각합니다.



답변

항상 기본값은 NOT EXISTS입니다.

실행 계획은 현재 동일 할 수 있지만 나중에 두 열을 변경하여 NULLs 를 허용 하면 NOT IN버전에 더 많은 작업을 수행해야합니다 ( NULL실제로 데이터에 s가 없는 경우에도 ) 및 s 있는 NOT IN경우 NULL의 의미 어쨌든 원하는 사람이 아닐 것입니다.

시도 Products.ProductID또는 [Order Details].ProductIDNULL의을은 NOT IN다음 쿼리와 동일하게 취급됩니다.

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId) 

정확한 계획은 다를 수 있지만 내 예제 데이터의 경우 다음을 얻습니다.

NULL이 아님

합리적으로 일반적인 오해는 연관 하위 쿼리가 조인에 비해 항상 “나쁜”것입니다. 중첩 된 루프 계획 (행별로 평가 된 하위 쿼리)을 강제로 적용 할 때도 가능하지만이 계획에는 반반 결합 논리 연산자가 포함됩니다. 안티 세미 조인은 중첩 루프로 제한되지 않지만 해시 또는 병합 (이 예와 같이) 조인도 사용할 수 있습니다.

/*Not valid syntax but better reflects the plan*/
SELECT p.ProductID,
       p.ProductName
FROM   Products p
       LEFT ANTI SEMI JOIN [Order Details] od
         ON p.ProductId = od.ProductId 

경우 [Order Details].ProductID입니다 NULL-able 쿼리는된다

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId)
       AND NOT EXISTS (SELECT *
                       FROM   [Order Details]
                       WHERE  ProductId IS NULL) 

s가 [Order Details]포함 된 경우 올바른 의미론 NULL ProductId은 결과를 반환하지 않기 때문입니다. 추가 반반 결합 및 행 수 스풀을 참조하여 계획에 추가되었는지 확인하십시오.

하나의 NULL

경우 Products.ProductID도되기 위해 변경 NULL-able 쿼리는된다

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId)
       AND NOT EXISTS (SELECT *
                       FROM   [Order Details]
                       WHERE  ProductId IS NULL)
       AND NOT EXISTS (SELECT *
                       FROM   (SELECT TOP 1 *
                               FROM   [Order Details]) S
                       WHERE  p.ProductID IS NULL) 

그 이유 는 하위 쿼리가 결과를 전혀 반환하지 않는 경우 (예 : 테이블이 비어있는 경우)를 제외하고NULL Products.ProductId 결과에 a 를 반환 하면 안되기 때문 입니다. 어떤 경우에해야합니다. 내 샘플 데이터 계획에서 이것은 다음과 같이 다른 반 세미 조인을 추가하여 구현됩니다.NOT IN[Order Details]

둘 다 NULL

이것의 효과는 Buckley에 의해 이미 링크 된 블로그 게시물에 표시됩니다 . 이 예에서는 논리적 읽기 수가 약 400에서 500,000으로 증가합니다.

또한 하나 NULL의 행 수를 0으로 줄일 수 있다는 사실 은 카디널리티 추정을 매우 어렵게 만듭니다. SQL Server가 이러한 상황이 발생한다고 가정하지만 실제로 NULL데이터에 행 이 없으면 나머지 실행 계획이 더 큰 쿼리의 일부인 경우 치명적으로 악화 될 수 있습니다. 부적절한 중첩 루프로 인해 값 비싼 하위 항목이 반복적으로 실행되는 예를 들어 트리 .

이것은 유일한 가능한 실행 계획이없는 NOT INA의 NULL그러나 -able 열입니다. 이 기사는 다른 것을 보여줍니다AdventureWorks2008 데이터베이스 에 대한 쿼리에 대한 .

를 들어 NOT INA의 NOT NULL열 또는 NOT EXISTS널 (NULL) 또는 비 널 (NULL) 열 중 하나에 대한 다음과 같은 계획을 제공합니다.

존재하지 않음

열이 NULL-able로 변경되면 NOT IN계획은 다음과 같습니다.

없음-널

내부 조인 연산자를 계획에 추가합니다. 이 장치는 여기설명되어 있습니다 . 이전의 단일 상관 ​​인덱스 검색을 Sales.SalesOrderDetail.ProductID = <correlated_product_id>외부 행당 두 개의 검색으로 변환하는 것이 전부 입니다. 추가가 켜져 WHERE Sales.SalesOrderDetail.ProductID IS NULL있습니다.

반 반 조인 아래 있으므로 행을 반환하면 두 번째 탐색이 발생하지 않습니다. 그러나을 Sales.SalesOrderDetail포함하지 않으면 NULL ProductID필요한 검색 작업 수의 두 배가됩니다.


답변

또한 NOT IN은 널이 될 때 NOT EXISTS와 동일하지 않습니다.

이 게시물은 그것을 잘 설명합니다

http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/

부속 조회가 하나의 널 (null)도 리턴하면 NOT IN은 행과 일치하지 않습니다.

NOT IN 조작의 실제 세부 사항을 보면이 이유를 찾을 수 있습니다.

예를 들어, t라는 테이블에 4 개의 행이 있고 값이 1..4 인 ID라는 열이 있다고 가정 해 봅시다.

WHERE SomeValue NOT IN (SELECT AVal FROM t)

에 해당

WHERE SomeValue != (SELECT AVal FROM t WHERE ID=1)
AND SomeValue != (SELECT AVal FROM t WHERE ID=2)
AND SomeValue != (SELECT AVal FROM t WHERE ID=3)
AND SomeValue != (SELECT AVal FROM t WHERE ID=4)

AVal은 ID = 4 인 경우 NULL입니다. 따라서! = 비교는 UNKNOWN을 반환합니다. AND에 대한 논리 진리표는 UNKNOWN과 TRUE가 UNKNOWN, UNKNOWN, FALSE가 FALSE임을 나타냅니다. 결과를 TRUE로 만들기 위해 UNKNOWN으로 AND 할 수있는 값이 없습니다.

따라서 해당 부속 쿼리의 행이 NULL을 리턴하면 전체 NOT IN 연산자는 FALSE 또는 NULL로 평가되며 레코드가 리턴되지 않습니다.


답변

실행 플래너가 동일하다고 말하면 동일합니다. 당신의 의도를 더 명확하게 할 수있는 방법을 사용하십시오 –이 경우에는 두 번째 방법.


답변

실제로, 이것이 가장 빠를 것이라고 믿습니다.

SELECT ProductID, ProductName
    FROM Northwind..Products p
          outer join Northwind..[Order Details] od on p.ProductId = od.ProductId)
WHERE od.ProductId is null


답변

약 120,000 개의 레코드가있는 테이블이 있고 행 수가 약 1500, 4000, 40000, 200 인 다른 4 개의 테이블에 존재하지 않는 테이블 (varchar 열과 일치) 만 선택하면됩니다. 관련된 모든 테이블에는 고유 인덱스가 있습니다. 관련 Varchar열에.

NOT IN약 10 분이 NOT EXISTS걸렸고 4 초가 걸렸습니다.

나는 10 분에 기여 수있는 몇 가지 조정되지 않은 부분을 가지고 있습니다 재귀 쿼리를 가지고 있지만, 4 초를 복용 다른 옵션은 나에게이어야, 설명 NOT EXISTS훨씬 더 나은 또는 그 이상이다 IN하고 EXISTS있습니다 정확히 같은 항상 가치 코드를 진행하기 전에 확인하십시오.


답변

옵티마이 저가 수행하려는 작업이 두 예제에서 모두 동일하다는 것을 알았으므로 특정 예제에서는 동일합니다. 그러나 사소한 예에서는 최적화 프로그램이이를 수행하지 않을 수 있으며,이 경우 경우에 따라 서로 선호하는 이유가 있습니다.

NOT IN외부 선택에서 여러 행을 테스트하는 경우 선호됩니다. NOT IN명령문 내부의 하위 쿼리 는 실행이 시작될 때 평가 될 수 있으며 임시 테이블은 필요할 때마다 하위 선택을 다시 실행하지 않고 외부 선택의 각 값에 대해 검사 할 수 있습니다.NOT EXISTS 명령문에 .

부속 조회 외부 선택과 상관 되어야 하는 경우, NOT EXISTS옵티마이 저가 동일한 기능을 수행하기 위해 임시 테이블을 작성하지 못하게하는 단순화를 발견 할 수 있기 때문에 바람직 할 수 있습니다.


답변

나는 사용하고 있었다

SELECT * from TABLE1 WHERE Col1 NOT IN (SELECT Col1 FROM TABLE2)

결과가 잘못되었다는 것을 알았습니다 (잘못된 결과는 없습니다). TABLE2.Col1에 NULL이 있으므로

검색어를로 변경하는 동안

SELECT * from TABLE1 T1 WHERE NOT EXISTS (SELECT Col1 FROM TABLE2 T2 WHERE T1.Col1 = T2.Col2)

나에게 정확한 결과를 주었다.

그 이후로 나는 모든 곳에서 NOT EXISTS를 사용하기 시작했습니다.