행 걸러내기: WHERE 제대로 쓰기

WHERE는 필요한 행만 남기는 절입니다. 조건을 여러 개 조합할 때의 우선순위와 NULL을 다룰 때의 규칙을 함께 살펴봅니다.

조건 조합하기

AND는 양쪽 조건이 모두 참인 행을, OR는 둘 중 하나만 참이어도 되는 행을 남깁니다. 아래 쿼리는 “결제됐거나 배송된 주문 중 금액이 500을 넘는 것”을 찾습니다.

1

SELECT order_number, status, total
FROM orders
WHERE (status = 'paid' OR status = 'shipped')
  AND total > 500
ORDER BY total DESC
LIMIT 20;
Planche에서 실행

결과 — 20행 중 처음 5행

order_numberstatustotal
ORD-2025-002767shipped30906.66
ORD-2022-001901shipped28151.01
ORD-2023-001772paid26797.87
ORD-2024-001907shipped26648.91
ORD-2025-002847paid25837.57

브라우저의 새 탭에서 Planche가 열리고, 샘플 데이터베이스가 로드된 채로 이 쿼리가 들어갑니다. 이 페이지의 다른 예제도 같은 탭을 다시 씁니다. F9를 누르기 전까지는 아무것도 실행되지 않습니다.

괄호를 뺀 쿼리도 실행해 보세요. 같은 조건을 썼어도 결과가 달라집니다.

2

-- The same words without the brackets. AND binds tighter than OR,
-- so this reads as: paid (any total) OR shipped-and-over-500.
SELECT order_number, status, total
FROM orders
WHERE status = 'paid' OR status = 'shipped' AND total > 500
ORDER BY total DESC
LIMIT 20;
Planche에서 실행

결과 — 20행 중 처음 5행

order_numberstatustotal
ORD-2025-002767shipped30906.66
ORD-2022-001901shipped28151.01
ORD-2023-001772paid26797.87
ORD-2024-001907shipped26648.91
ORD-2025-002847paid25837.57

결과가 달라지는 이유는 AND가 OR보다 먼저 계산되기 때문입니다. ×가 +보다 먼저 계산되는 것과 같습니다. 괄호가 없으면 데이터베이스는 이를 “금액과 무관하게 결제된 것 또는 배송되고 500을 넘는 것”으로 읽습니다. 그래서 소액 결제 주문도 결과에 들어갑니다.

여러 값 중 하나와 맞추기

같은 컬럼을 여러 값과 OR로 비교한다면 IN으로 간단히 쓸 수 있습니다.

3

SELECT order_number, status, total, ordered_at
FROM orders
WHERE status IN ('paid', 'shipped', 'delivered')
ORDER BY ordered_at DESC
LIMIT 20;
Planche에서 실행

결과 — 20행 중 처음 5행

order_numberstatustotalordered_at
ORD-2025-002480shipped6333.772025-12-28 23:52:14
ORD-2025-000505delivered2276.512025-12-28 11:16:39
ORD-2025-001581delivered2645.062025-12-28 06:12:38
ORD-2025-000995delivered2584.022025-12-28 05:47:53
ORD-2025-003219delivered561.482025-12-28 03:20:37

status IN ('paid', 'shipped', 'delivered')는 같은 컬럼을 비교하는 OR 세 개와 같습니다. 반대 조건에는 NOT IN을 쓰지만, NULL이 섞일 때 주의할 점이 있어 서브쿼리 레슨에서 따로 다룹니다.

범위

BETWEEN a AND b는 >= a AND <= b의 축약입니다. 양쪽 끝을 모두 포함합니다.

4

SELECT order_number, ordered_at, total
FROM orders
WHERE ordered_at BETWEEN '2025-01-01' AND '2025-01-31 23:59:59'
ORDER BY ordered_at
LIMIT 20;
Planche에서 실행

결과 — 20행 중 처음 5행

order_numberordered_attotal
ORD-2025-0034122025-01-01 00:18:522806.57
ORD-2025-0036162025-01-01 11:18:146955.62
ORD-2025-0015032025-01-01 23:43:345837.23
ORD-2025-0003172025-01-02 05:08:3215130.7
ORD-2025-0012982025-01-02 18:36:325045.04

패턴

LIKE는 패턴과 비교합니다. %는 임의의 문자열, _는 정확히 한 글자입니다.

5

SELECT first_name, last_name, email
FROM customers
WHERE email LIKE '%@inbox.example'
ORDER BY last_name
LIMIT 20;
Planche에서 실행

결과 — 20행 중 처음 5행

first_namelast_nameemail
FreyaAhmedfreya.ahmed@inbox.example
HugoAhmedhugo.ahmed@inbox.example
JonasAhmedjonas.ahmed@inbox.example
KenjiAhmedkenji.ahmed@inbox.example
LiamAhmedliam.ahmed@inbox.example

값이 없을 때

이 고객들 중 279명은 전화번호가 기록돼 있지 않습니다. 그 279명을 찾는 쿼리입니다.

6

SELECT id, first_name, last_name, phone
FROM customers
WHERE phone IS NULL
LIMIT 20;
Planche에서 실행

결과 — 20행 중 처음 5행

idfirst_namelast_namephone
2MateoCostaNULL
9SvenSmithNULL
21LaylaFischerNULL
25FreyaNguyenNULL
28PriyaJangNULL

그리고 아래는 대부분이 처음에 쓰는 쿼리입니다.

7

-- 279 customers have no phone number, and this finds none of them.
SELECT id, first_name, last_name, phone
FROM customers
WHERE phone = NULL;
Planche에서 실행

결과 없음

결과는 0행입니다. 오류도 경고도 나오지 않습니다.

NULL은 값이 아니라 값이 없음입니다. 그래서 NULL과의 어떤 비교도 참이 될 수 없습니다. phone = NULL은 "이 알 수 없는 것이 저 알 수 없는 것과 같은가"를 묻는 셈이고, 정직한 답은 "모름"이며, 모름은 참이 아니므로 그 행은 버려집니다. NULL = NULL조차 참이 아닙니다. 동작하는 건 IS NULL과 IS NOT NULL뿐입니다.

여기까지로 할 수 있는 것

한 테이블 안에서라면 거의 모든 조건을 표현할 수 있습니다. 남은 한계는 여전히 모든 것이 한 테이블 안에 있다는 것이고, 정작 물어볼 가치가 있는 질문들은 대개 그렇지 않습니다.

직접 해보기

  1. 한국이나 일본에 있으면서 마케팅 메일 수신에 동의한(marketing_opt_in이 1) 고객을, 국가 다음 성 순으로 정렬해 보여주세요.

    정답 보기

    정답 보기

    SELECT first_name, last_name, country
    FROM customers
    WHERE country IN ('KR', 'JP')
      AND marketing_opt_in = 1
    ORDER BY country, last_name
    LIMIT 20;
    Planche에서 실행

    결과 — 20행 중 처음 5행

    first_namelast_namecountry
    AishaAhmedJP
    IsabellaAhmedJP
    IsabellaBrownJP
    TheoBrownJP
    NoraChoiJP

    IN이 OR 하나를 줄여줍니다. 여기서는 AND 하나가 조건 둘을 잇는 것뿐이라 괄호가 필요 없습니다. 정렬 키 둘이 국가를 먼저, 그 안에서 성을 정렬합니다.

  2. 2024년에 들어온 주문 중 취소되거나 환불된 것을, 오래된 것부터 보여주세요.

    정답 보기

    정답 보기

    SELECT order_number, status, ordered_at
    FROM orders
    WHERE ordered_at >= '2024-01-01'
      AND ordered_at <  '2025-01-01'
      AND status IN ('cancelled', 'refunded')
    ORDER BY ordered_at
    LIMIT 20;
    Planche에서 실행

    결과 — 20행 중 처음 5행

    order_numberstatusordered_at
    ORD-2024-002563cancelled2024-01-02 11:41:56
    ORD-2024-002814cancelled2024-01-08 06:10:29
    ORD-2024-003123refunded2024-01-10 15:59:33
    ORD-2024-003024refunded2024-01-11 19:36:33
    ORD-2024-002258refunded2024-01-12 15:43:55

    반열린 구간이 핵심입니다. >= '2024-01-01' AND < '2025-01-01'은 12월 31일의 마지막 순간까지 포함하는데, BETWEEN '2024-01-01' AND '2024-12-31'은 자정에서 끊깁니다.