S E P H ' S
[MySQL] #1 IN / EXISTS / NOT IN / NOT EXISTS 본문
먼저 포스트를 작성하기 위해 작성한 DB Table의 정보이다.
IN / EXISTS 는 서브쿼리를 반환하기 위한 연산자이다. 하지만 두 연산자는 수행 매커니즘이 다르다. 다음을 보면 두 연산자의 매커니즘의 차이를 이해하는데 도움이 될 것이다.
1. select ( 1 != null ) || 2<3;
2. select ( 1 != null ) || 2>3;
3. select ( 1 != null ) && 2>3;
4. select ( 1 != null ) && 2<3;
각 SQL 문의 수행결과를 보고 알 수 있는 것은 SQL문이 null과 연산을 할 때, 1,3번과 같이 확정적인 연산 결과는 false, true (0,1)를 반환하지만 결과를 알 수 없는 확정적이지 않은 연산 결과는 NULL을 반환 한다는 것이다.
IN
in은 in 다음에 오는 쿼리가 먼저 수행된다.
select customer_name
from customer
where customer_id in (select customer_id from customer_order);
즉 위의 SQL문은 "select customer_id from customer_order" 을 먼저 수행하고 그 결과 값으로 customer_name을 조회하게 된다.
in 안의 SQL은 2, 2, null을 반환하고
select customer_name
from customer
where customer_id in (2,2,null);
위의 SQL과 동일한 것이다. 위의 결과는 다음 <그림8>과 같다. 이러한 in은 or 연산과 매커니즘이 동일하다.
NOT IN
다음의 NOT IN을 포함한 SQL문을 수행해보면 본문을 시작하면서 설명했던 것들과 함께 이해가 잘 될 것이다.
select customer_name
from customer
where customer_id not in (select customer_id from customer_order);
in이 쿼리 결과를 포함하는 결과를 반환했다면 not in 은 말그대로 포함하지 않은 결과를 반환하는 것이다. 하지만 위의 SQL문을 실행하면 다음과 같은 결과가 나온다.
결과값이 아무것도 없는 것을 볼 수 있다. in 내부의 쿼리를 실행하면
select customer_name
from customer
where customer_id not in (2,2,null);
이와 같은 결과일 것이고 이 요소들과 일치하지 않아야 결과로 반환될 것이다. 하지만 (1 != null)과 같이 null과 연산을 하는 부분에서 결과가 확정적이지 않은 연산이기 때문에 null을 반환하고 아무것도 출력되지 않는 것이다. 이를 수정하려면 다음과 같이 SQL문을 작성하면 된다.
select customer_name
from customer
where customer_id not in (select customer_id from customer_order
where customer_id is not null);
EXISTS / NOT EXISTS
이런 매커니즘의 차이 외에도 속도면에서도 exists 연산자가 in보다 우수하여 더 많이 사용한다. in이 in () 내부의 SQL부터 수행했다면 exists는 바깥쪽의 SQL을 먼저 수행하고, 수행된 결과에 대해서 각각 exists 절을 모두 수행한다. 이러한 exists 절은 내부적으로 join 매커니즘을 수행한다.
select c.customer_name
from customer c
where exists (
select o.customer_id from customer_order o
where c.customer_id = o.customer_id
);
select c.customer_name
from customer c
where not exists (
select o.customer_id from customer_order o
where c.customer_id = o.customer_id
);
NOT EXISTS가 별다른 처리 없이 <그림12>와 같이 결과를 내는 이유는 null 인부분에 대해서
"select c.customer_name from customer c where = null;" 과 같이 수행되는데 not exists 바깥의 SQL 결과에 대해서 각각 처리가 되므로 null과 연산이 된 부분은 제외하고 결과가 반환이 되는 것이다.