조건 조합하기
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;
결과 — 20행 중 처음 5행
| order_number | status | total |
|---|---|---|
| ORD-2025-002767 | shipped | 30906.66 |
| ORD-2022-001901 | shipped | 28151.01 |
| ORD-2023-001772 | paid | 26797.87 |
| ORD-2024-001907 | shipped | 26648.91 |
| ORD-2025-002847 | paid | 25837.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;
결과 — 20행 중 처음 5행
| order_number | status | total |
|---|---|---|
| ORD-2025-002767 | shipped | 30906.66 |
| ORD-2022-001901 | shipped | 28151.01 |
| ORD-2023-001772 | paid | 26797.87 |
| ORD-2024-001907 | shipped | 26648.91 |
| ORD-2025-002847 | paid | 25837.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;
결과 — 20행 중 처음 5행
| order_number | status | total | ordered_at |
|---|---|---|---|
| ORD-2025-002480 | shipped | 6333.77 | 2025-12-28 23:52:14 |
| ORD-2025-000505 | delivered | 2276.51 | 2025-12-28 11:16:39 |
| ORD-2025-001581 | delivered | 2645.06 | 2025-12-28 06:12:38 |
| ORD-2025-000995 | delivered | 2584.02 | 2025-12-28 05:47:53 |
| ORD-2025-003219 | delivered | 561.48 | 2025-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;
결과 — 20행 중 처음 5행
| order_number | ordered_at | total |
|---|---|---|
| ORD-2025-003412 | 2025-01-01 00:18:52 | 2806.57 |
| ORD-2025-003616 | 2025-01-01 11:18:14 | 6955.62 |
| ORD-2025-001503 | 2025-01-01 23:43:34 | 5837.23 |
| ORD-2025-000317 | 2025-01-02 05:08:32 | 15130.7 |
| ORD-2025-001298 | 2025-01-02 18:36:32 | 5045.04 |
패턴
LIKE는 패턴과 비교합니다. %는 임의의 문자열, _는 정확히 한 글자입니다.
5
SELECT first_name, last_name, email
FROM customers
WHERE email LIKE '%@inbox.example'
ORDER BY last_name
LIMIT 20;
결과 — 20행 중 처음 5행
| first_name | last_name | |
|---|---|---|
| Freya | Ahmed | freya.ahmed@inbox.example |
| Hugo | Ahmed | hugo.ahmed@inbox.example |
| Jonas | Ahmed | jonas.ahmed@inbox.example |
| Kenji | Ahmed | kenji.ahmed@inbox.example |
| Liam | Ahmed | liam.ahmed@inbox.example |
값이 없을 때
이 고객들 중 279명은 전화번호가 기록돼 있지 않습니다. 그 279명을 찾는 쿼리입니다.
6
SELECT id, first_name, last_name, phone
FROM customers
WHERE phone IS NULL
LIMIT 20;
결과 — 20행 중 처음 5행
| id | first_name | last_name | phone |
|---|---|---|---|
| 2 | Mateo | Costa | NULL |
| 9 | Sven | Smith | NULL |
| 21 | Layla | Fischer | NULL |
| 25 | Freya | Nguyen | NULL |
| 28 | Priya | Jang | NULL |
그리고 아래는 대부분이 처음에 쓰는 쿼리입니다.
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;
결과 없음
결과는 0행입니다. 오류도 경고도 나오지 않습니다.
NULL은 값이 아니라 값이 없음입니다. 그래서 NULL과의 어떤 비교도 참이 될 수 없습니다. phone = NULL은 "이 알 수 없는 것이 저 알 수 없는 것과 같은가"를 묻는 셈이고, 정직한 답은 "모름"이며, 모름은 참이 아니므로 그 행은 버려집니다. NULL = NULL조차 참이 아닙니다. 동작하는 건 IS NULL과 IS NOT NULL뿐입니다.
여기까지로 할 수 있는 것
한 테이블 안에서라면 거의 모든 조건을 표현할 수 있습니다. 남은 한계는 여전히 모든 것이 한 테이블 안에 있다는 것이고, 정작 물어볼 가치가 있는 질문들은 대개 그렇지 않습니다.