이 중 어떤 쿼리가 더 빠릅니까?
존재하지 않음 :
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
입니다.
실행 계획은 현재 동일 할 수 있지만 나중에 두 열을 변경하여 NULL
s 를 허용 하면 NOT IN
버전에 더 많은 작업을 수행해야합니다 ( NULL
실제로 데이터에 s가 없는 경우에도 ) 및 s 가 있는 NOT IN
경우 NULL
의 의미 어쨌든 원하는 사람이 아닐 것입니다.
시도 Products.ProductID
또는 [Order Details].ProductID
수 NULL
의을은 NOT IN
다음 쿼리와 동일하게 취급됩니다.
SELECT ProductID,
ProductName
FROM Products p
WHERE NOT EXISTS (SELECT *
FROM [Order Details] od
WHERE p.ProductId = od.ProductId)
정확한 계획은 다를 수 있지만 내 예제 데이터의 경우 다음을 얻습니다.
합리적으로 일반적인 오해는 연관 하위 쿼리가 조인에 비해 항상 “나쁜”것입니다. 중첩 된 루프 계획 (행별로 평가 된 하위 쿼리)을 강제로 적용 할 때도 가능하지만이 계획에는 반반 결합 논리 연산자가 포함됩니다. 안티 세미 조인은 중첩 루프로 제한되지 않지만 해시 또는 병합 (이 예와 같이) 조인도 사용할 수 있습니다.
/*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
은 결과를 반환하지 않기 때문입니다. 추가 반반 결합 및 행 수 스풀을 참조하여 계획에 추가되었는지 확인하십시오.
경우 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]
이것의 효과는 Buckley에 의해 이미 링크 된 블로그 게시물에 표시됩니다 . 이 예에서는 논리적 읽기 수가 약 400에서 500,000으로 증가합니다.
또한 하나 NULL
의 행 수를 0으로 줄일 수 있다는 사실 은 카디널리티 추정을 매우 어렵게 만듭니다. SQL Server가 이러한 상황이 발생한다고 가정하지만 실제로 NULL
데이터에 행 이 없으면 나머지 실행 계획이 더 큰 쿼리의 일부인 경우 치명적으로 악화 될 수 있습니다. 부적절한 중첩 루프로 인해 값 비싼 하위 항목이 반복적으로 실행되는 예를 들어 트리 .
이것은 유일한 가능한 실행 계획이없는 NOT IN
A의 NULL
그러나 -able 열입니다. 이 기사는 다른 것을 보여줍니다AdventureWorks2008
데이터베이스 에 대한 쿼리에 대한 .
를 들어 NOT IN
A의 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를 사용하기 시작했습니다.