[SQL] IN과 EXISTS의 차이
IN과 EXISTS는 언제 사용하는가?
두 연산자는 모두 WHERE 절에서 조건에 맞는 데이터를 걸러낼 때 사용된다. 하지만 내부 동작 방식이 다르기 때문에 상황에 따라 더 적합한 연산자가 달라진다.
예시와 함께 살펴보자. 전자상거래에서 이용되는 회원/주문/주문상품 3개의 테이블이 있고, 각 테이블마다 다음과 같은 컬럼을 가진다고 하자.
회원(컬럼) : customers(customer_id, name, status)
주문(컬럼) : orders(order_id, customer_id, order_date, total_amount)
주문상품(컬럼) : order_items(order_id, product_id, qty)
IN
- 괄호 안에 나열된 값 또는 서브쿼리가 반환한 결과 목록에 특정 값이 포함되어 있는지 확인할 때 사용한다.
- 실행 시 SQL 엔진은 내부적으로 이를 여러 개의 OR 조건으로 변환해 비교한다.
- 비교 대상 컬럼의 실제 값을 기반으로 직접 비교하는 방식이다.
- 실행 순서: IN 서브쿼리 → 메인 쿼리
(즉, 비교할 목록을 먼저 만든 뒤 메인 쿼리에서 해당 값들이 있는지 확인)
ex1) 값을 직접 나열하는 IN : 값 목록이 작고 명확할 때
활성/대기 고객을 조회하는 경우
SELECT customer_id, name, status
FROM customers
WHERE status IN ('ACTIVE', 'PENDING');
status = 'ACTIVE' OR status = 'PENDING' 처럼 이해하면 된다.
ex2) 서브쿼리 결과를 활용하는 IN
주문을 한 적 있는 고객 목록을 조회하는 경우(단, 주문 금액 100만원 이상 주문이 있는 고객만 조회)
SELECT customer_id, name
FROM customers
WHERE customer_id IN (
SELECT customer_id
FROM orders
WHERE total_amount >= 1000000
);
서브쿼리가 먼저 실행되어 customer_id 목록을 만들고 바깥에서 IN (목록)으로 비교한다.
EXISTS
- 주어진 조건을 만족하는 레코드가 존재하는지 여부만 확인한다.
- IN과 달리 직접 값을 나열할 수 없고 반드시 서브쿼리만 사용한다.
- 서브쿼리에서 조건을 만족하는 행이 하나라도 발견되면 즉시 TRUE를 반환하고 더 이상 조회하지 않는다.
(그래서 서브쿼리의 SELECT 절에는 1이나 'X'처럼 아무 값을 넣어도 상관없다.) - 실행 순서: 메인 쿼리 → EXISTS 서브쿼리
(즉, 메인 쿼리의 각 행을 기준으로 존재 여부를 검사)
✔ EXISTS 서브쿼리는 실제 값을 반환하지 않고, 단지 TRUE/FALSE 판정만 메인 쿼리에 넘긴다.
✔ 반환되는 행은 항상 메인 쿼리의 행이다. → 테이블 B에 몇 개의 행이 있든 상관없고, 테이블 A의 한 행은 한 번만 반환.
(이때 A는 메인 쿼리에서 결과로 반환되는 테이블, B는 EXISTS 안에서 존재 여부를 검사하는 테이블이다.)
✔ A 테이블의 각 행마다 개별적으로 EXISTS를 평가한다. → TRUE인 A의 행은 모두 결과에 포함, FALSE인 A의 행은 제외.
ex1) 주문이 하나라도 존재하는 고객 조회
SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (
SELECT 1 # 아무 값이어도 됨
FROM orders o
WHERE o.customer_id = c.customer_id # 주문이 있는지만 판단
);
반환되는 행은 customers(c) 의 행이다.
ex2) 최근 30일 내 주문이 있는 고객 조회
SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o # 각 고객(c)에 대해 “최근 30일 주문이 있나?”만 확인
WHERE o.customer_id = c.customer_id
AND o.order_date >= CURRENT_DATE - INTERVAL 30 DAY # 또는 DATE_SUB(CURRENT_DATE, INTERVAL 30 DAY)
);
INTERVAL은 날짜 및 시간 값에 대한 연산을 수행할 때 사용되는 문법이다.
INTERVAL 문법 : [date/time] [+/-] INTERVAL [더하거나 빼고자 하는 값] [interval unit]
ex) SELECT NOW() + INTERVAL 1 YEAR;
ex3) 주문 상품이 1개라도 포함된 주문만 조회 (주문 하나에 상품이 여러 개여도 주문은 1번만 나온다.)
SELECT o.order_id, o.customer_id, o.order_date
FROM orders o
WHERE EXISTS (
SELECT 1
FROM order_items oi
WHERE oi.order_id = o.order_id
);
order_items에 동일 order_id가 여러 줄이어도 결과는 orders(o)의 행이므로 주문은 1번만 반환한다.
EXISTS과 IN의 비교
- IN은 서브쿼리의 결과값을 이용해 직접 값을 비교하는 방식인 반면, EXISTS는 존재 여부만 판단하기 때문에 불필요한 조회를 하지 않아 상대적으로 효율적이다.
- 특히 서브쿼리 결과가 매우 큰 경우에는 EXISTS가 일반적으로 더 좋은 성능을 낸다.
- 하지만 데이터 양이 크지 않거나 단순 비교라면 둘의 성능은 거의 비슷하다.
ex) 주문을 한 적 있는 고객 조회 (IN 버전 vs EXISTS 버전)
-- IN 버전
SELECT c.customer_id, c.name
FROM customers c
WHERE c.customer_id IN (
SELECT o.customer_id
FROM orders o
);
-- EXISTS 버전
SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);
두 쿼리 결과는 같을 수 있지만, 일반적으로 orders가 매우 크고, 고객별 존재 여부만 확인하면 되는 상황에서는 EXISTS가 더 유리한 경우가 많다.
Reference
https://bsssss.tistory.com/1419
[SQL] IN, EXISTS 비교 및 속도 비교
IN in은 입력된 값들 중 하나라도 일치하는 것이 있으면 조회된다. 쿼리가 실행될 때 내부적으로 or 연산자로 변경되어 실행되는 것이다. select e.emp_no from employees.employees e where emp_no in (SELECT s.emp_no
bsssss.tistory.com
SQL에서 EXISTS와 IN의 차이는 뭘까? (+INNER JOIN)
쿼리를 작성하다 보니 IN과 EXISTS 중 어느 걸 써야 더 효율적일지 궁금해졌다. 오늘은 이 둘의 동작방식과 어떤 상황에서 어떤 연산자를 사용해야 더 효율적일지 알아보자.
velog.io