NOT EXISTS의 동작 원리: 서브쿼리의 반복 실행 이해하기SQL 쿼리를 작성하다 보면 NOT EXISTS를 사용하여 특정 조건을 만족하지 않는 행을 필터링하는 경우가 많다. NOT EXISTS는 마치 프로그래밍 언어의 for 루프처럼 외부 쿼리의 각 행에 대해 서브쿼리를 반복적으로 실행한다. 이 글에서는 NOT EXISTS의 동작 원리와 이를 활용하는 방법, 그리고 IN, NOT IN, EXISTS, NOT EXISTS의 차이점과 장단점에 대해 자세히 설명한다.
NOT EXISTS란 무엇인가?NOT EXISTS는 서브쿼리가 한 행이라도 결과를 반환하지 않는 경우 참이 되는 조건문이다. 이는 특정 조건을 만족하지 않는 행을 선택하는 데 유용하다. NOT EXISTS를 사용할 때, SQL 엔진은 외부 쿼리의 각 행에 대해 서브쿼리를 실행하여 조건을 평가한다.
예제 테이블을 설정하고 데이터를 삽입해 보자. 다음과 같은 두 개의 테이블이 있다고 가정한다: item_info와 item_tree.
CREATE TABLE item_info (
item_id INT PRIMARY KEY,
item_name VARCHAR(50)
);
CREATE TABLE item_tree (
item_id INT,
parent_item_id INT
);
INSERT INTO item_info (item_id, item_name) VALUES
(1, 'Item A'),
(2, 'Item B'),
(3, 'Item C');
INSERT INTO item_tree (item_id, parent_item_id) VALUES
(1, 2),
(2, 3),
(3, NULL),
(4, 1);
NOT EXISTS 사용 예제다음 쿼리는 item_info 테이블에서 item_tree의 parent_item_id로 참조되지 않는 항목들을 선택한다.
SELECT * FROM item_info
WHERE NOT EXISTS (
SELECT 1 FROM item_tree
WHERE item_tree.parent_item_id = item_info.item_id
);
위 쿼리는 item_info 테이블의 각 행에 대해 다음과 같은 과정을 거친다.
첫 번째 행 (item_id = 1):
SELECT 1 FROM item_tree WHERE item_tree.parent_item_id = 1item_tree 테이블에서 parent_item_id = 1인 행이 존재하므로, 서브쿼리는 결과를 반환한다.NOT EXISTS 조건은 거짓이 되므로, 이 행은 최종 결과에서 제외된다.두 번째 행 (item_id = 2):
SELECT 1 FROM item_tree WHERE item_tree.parent_item_id = 2item_tree 테이블에서 parent_item_id = 2인 행이 존재하므로, 서브쿼리는 결과를 반환한다.NOT EXISTS 조건은 거짓이 되므로, 이 행은 최종 결과에서 제외된다.세 번째 행 (item_id = 3):
SELECT 1 FROM item_tree WHERE item_tree.parent_item_id = 3item_tree 테이블에서 parent_item_id = 3인 행이 존재하지 않으므로, 서브쿼리는 결과를 반환하지 않는다.NOT EXISTS 조건은 참이 되므로, 이 행은 최종 결과에 포함된다.이와 같은 방식으로 NOT EXISTS는 외부 쿼리의 각 행에 대해 서브쿼리를 반복적으로 실행하여 조건을 평가한다.
IN, NOT IN, EXISTS, NOT EXISTS의 차이점과 장단점SQL에서는 다양한 방법으로 서브쿼리를 사용할 수 있다. 그 중에서 IN, NOT IN, EXISTS, NOT EXISTS의 차이점과 장단점을 살펴보자.
IN과 EXISTSIN은 서브쿼리가 반환한 결과 집합에 외부 쿼리의 값이 존재하는지 확인한다. 반면 EXISTS는 서브쿼리가 결과를 반환하는지 여부를 확인한다.
예제:
-- `IN` 사용 예
SELECT * FROM item_info
WHERE item_id IN (
SELECT parent_item_id FROM item_tree
);
-- `EXISTS` 사용 예
SELECT * FROM item_info
WHERE EXISTS (
SELECT 1 FROM item_tree
WHERE item_tree.parent_item_id = item_info.item_id
);
장단점:
IN은 서브쿼리의 결과를 메모리에 로드하여 비교하기 때문에 작은 결과 집합에 유리하다.EXISTS는 서브쿼리가 조건을 만족하는 첫 번째 행을 찾으면 즉시 종료되므로, 큰 결과 집합에 유리하다.NOT IN과 NOT EXISTSNOT IN은 서브쿼리가 반환한 결과 집합에 외부 쿼리의 값이 존재하지 않는지 확인한다. NOT EXISTS는 서브쿼리가 결과를 반환하지 않는지 여부를 확인한다.
예제:
-- `NOT IN` 사용 예
SELECT * FROM item_info
WHERE item_id NOT IN (
SELECT parent_item_id FROM item_tree
);
-- `NOT EXISTS` 사용 예
SELECT * FROM item_info
WHERE NOT EXISTS (
SELECT 1 FROM item_tree
WHERE item_tree.parent_item_id = item_info.item_id
);
장단점:
NOT IN은 서브쿼리의 결과에 NULL 값이 포함되면 의도치 않은 결과를 초래할 수 있다.NOT EXISTS는 서브쿼리가 결과를 반환하지 않는 경우만 참이 되므로 NULL 값을 안전하게 처리할 수 있다.SQL에서 NOT EXISTS는 매우 강력한 도구다. 이를 사용하여 특정 조건을 만족하지 않는 행을 쉽게 필터링할 수 있다. 외부 쿼리의 각 행에 대해 서브쿼리를 반복적으로 실행하는 방식으로 동작하므로, 마치 프로그래밍 언어의 for 루프처럼 생각할 수 있다. 또한, IN, NOT IN, EXISTS, NOT EXISTS의 차이점과 장단점을 이해하면 상황에 맞는 최적의 쿼리를 작성할 수 있다.