상관 서브 쿼리
상관 서브 쿼리는 이름 그대로, 메인 쿼리와 서브 쿼리가 서로 연관 관계를 맺고 동작하는 서브 쿼리다. 서브 쿼리가 독립적으로 실행될 수 없고, 메인 쿼리의 값을 참조하여 실행되는 것이 특징이다.
- 비상관 서브쿼리 (Non-correlated Subquery): 독립적으로 실행될 수 있는 서브쿼리로, 메인 쿼리의 값과 상관없이 실행된다.
- 상관 서브쿼리 (Correlated Subquery): 메인 쿼리의 값과 연관되어 실행되는 서브쿼리로, 메인 쿼리의 값이 변경될 때마다 서브쿼리도 재실행된다.
즉 서브 쿼리가 메인 쿼리의 행 수 만큼 반복 실행될 수 있다.
IN 을 사용하는 방법
EXISTS 를 사용한 더 효율적인 방법
IN 방식은 Orders 테이블이 매우 클 경우, 즉 주문량이 수백만, 수천만 건에 달하면 성능 문제를 일으킬 수 있다. orders 를 조회하는 서브 쿼리가 반환하는 지금까지 주문한 product_id 목록 전체를 메모리에 저장한 뒤, 메인 쿼리의 각 행과 비교해야 하기 때문이다.
이럴때 EXISTS 를 사용하면 효율적으로 쿼리를 실행할 수 있다.
EXISTS 는 서브 쿼리가 반환하는 결과값 자체에는 관심이 없고, 오직 서브 쿼ㅣ리의 결과로 행이 하나라도 존재하는지 여부만 체크하기 때문이다.
EXISTS 연산
- 서브 쿼리 결과 행이 1개 이상이면 TRUE
- 서브 쿼리 결과 행이 0개이면 FALSE
즉 EXISTS 는 서브 쿼리가 결과를 반환하는지 여부만 확인한다.
SELECT
product_id,
name,
price
FROM
products p
WHERE
EXISTS (
SELECT 1
FROM Orders
WHERE Orders.product_id = Products.product_id
)실무적 관점: 트레이드오프
IN방식은 서브 쿼리가 반환하는 결과를 메모리에 저장하고 메인 쿼리의 각 행과 비교해야 하기 때문에 메모리 사용량이 많고, 성능이 저하될 수 있다.EXISTS방식은 서브 쿼리의 결과로 행이 하나라도 존재하는지 여부만 확인하기 때문에 메모리 사용량이 적고, 성능이 높다.EXISTS방식은 서브 쿼리의 결과가 없더라도 메인 쿼리의 실행을 중단하지 않기 때문에, 서브 쿼리의 결과가 없더라도 메인 쿼리의 실행을 계속할 수 있다.EXISTS방식은 서브 쿼리의 결과가 없더라도 메인 쿼리의 실행을 중단하지 않기 때문에, 서브 쿼리의 결과가 없더라도 메인 쿼리의 실행을 계속할 수 있다.
NOT EXISTS 연산
- 서브 쿼리 결과 행이 0개이면 TRUE
- 서브 쿼리 결과 행이 1개 이상이면 FALSE
즉 NOT EXISTS 는 서브 쿼리가 결과를 반환하지 않는지 여부만 확인한다.
상관 서브 쿼리와 성능
상관 서브 쿼리는 복잡한 로직을 매우 직관적으로 표현할 수 있게 해주지만 성능에 주의해야 한다. 메인 쿼리의 행 수만큼 서브 쿼리가 반복 실행될 수 있기 때문에 메인 쿼리가 다루는 데이터 양이 많아지면 쿼리 전체의 성능이 급격히 저하될 수 있다.
많은 경우, 상관 서브 쿼리는 JOIN(LEFT JOIN, GROUP BY)으로 동일한 결과를 얻도록 재작성할 수 있으며 데이터베이스 옵티마이저가 JOIN 을 더 효율적으로 처리하는 경우가 많다.
하지만 EXISTS 는 특정 조건에 맞는 데이터가 있는지 확인만 하고 넘어가는 특성 덕분에 IN 이나 JOIN 보다 훨씬 효율적으로 동작하는 상황도 많다.
결론적으로 상관 서브 쿼리는 성능 이슈를 인지하고, JOIN 으로 표현하기 너무 복잡하거나 EXISTS 를 통해 더 효율적인 실행이 가능할 때 적절히 사용하는 것이 중요하다.