SQL WHERE 절은 표에서 조건이 참(TRUE)인 행만 남기는 필터입니다. 지난 글의 SELECT 가 「어떤 열을 볼지」를 정했다면 WHERE 는 「어떤 행을 볼지」를 정하는데요, 비교 연산자와 AND·OR, IN·BETWEEN·LIKE, IS NULL 만 알아도 「11월 첫 주 검색광고 주문 중 1만 원 이상」 같은 조건을 한 줄로 걸 수 있습니다. 저도 SQL 을 처음 배우면서 엑셀 필터처럼 조건만 고르면 끝일 줄 알았는데요, 빈 값(NULL)이 조건에서 조용히 빠지는 규칙 때문에 결과 행 수가 생각과 달라지는 경우가 있었습니다. 이번 글에서는 WHERE 의 기본형부터 조건을 묶는 법, NULL 을 다루는 법까지 예시 표로 직접 따라 해 보겠습니다. 쿼리는 BigQuery(GoogleSQL) 문법으로 적었습니다. 지난 글: SQL SELECT 문 기초: SELECT·FROM 으로 원하는 열만 꺼내기
SQL 기초 시리즈 7/12 · 전체 커리큘럼
오늘 배울 개념
WHERE 는 엑셀의 자동 필터와 비슷합니다. 주문 시트에서 채널 열의 필터 단추를 눌러 「검색광고」만 체크하면 나머지 줄이 숨겨지죠. WHERE 도 조건을 적으면 그 조건을 만족하는 행만 결과에 남기는데요, 엑셀 필터가 원본 줄을 숨길 뿐인 것처럼 WHERE 도 원본 표는 그대로 두고 걸러진 결과 표만 돌려줍니다. BigQuery 쿼리 문법 문서는 WHERE 를 「FROM 절의 결과를 거르는 절」이라고 설명하고, 조건식이 TRUE 인 행만 포함하고 FALSE 나 NULL 인 행은 버린다고 적고 있습니다. 이 「NULL 인 행도 버린다」는 한 줄이 오늘 글의 핵심인데요, 뒤에서 다시 꺼내겠습니다.
개념 그림: WHERE 는 조건이 TRUE 인 행만 통과시키고, FALSE 와 NULL 은 함께 걸러 냅니다
따라 해 보기: SQL WHERE 절로 행 거르기
이번에도 문구점 주문 표 shop.orders 를 씁니다(가상 데이터). 지난 글보다 행을 두 개 늘렸고, 금액(amount)은 미리 계산해 넣었습니다. 쿠폰을 쓰지 않은 주문은 coupon 이 비어 있는데요, 이 빈 값이 NULL 입니다.
orders
| order_id | order_date | channel | product | amount | coupon |
|---|---|---|---|---|---|
| 1 | 2026-11-02 | 검색광고 | 볼펜 | 6000 | NULL |
| 2 | 2026-11-02 | SNS | 노트 | 6000 | WELCOME |
| 3 | 2026-11-03 | 검색광고 | 노트 | 3000 | NULL |
| 4 | 2026-11-03 | 이메일 | 볼펜 | 15000 | VIP10 |
| 5 | 2026-11-04 | SNS | 노트 세트 | 9000 | NULL |
| 6 | 2026-11-05 | 검색광고 | 형광펜 | 10000 | WELCOME |
| 7 | 2026-11-06 | SNS | 형광펜 세트 | 12000 | NULL |
| 8 | 2026-11-06 | 이메일 | 노트 | 3000 | NULL |
1. 비교 연산자와 AND·OR: 「검색광고나 SNS 로 들어온 9,000원 이상 주문」을 찾아보겠습니다. 비교 연산자는 =, <, >, <=, >= 와 「같지 않다」를 뜻하는 !=(또는 <>)가 있고, BigQuery 연산자 문서에 따르면 모두 결과를 TRUE·FALSE 같은 BOOL 값으로 돌려줍니다.
SELECT order_id, channel, amount
FROM shop.orders
WHERE (channel = '검색광고' OR channel = 'SNS')
AND amount >= 9000;| order_id | channel | amount |
|---|---|---|
| 5 | SNS | 9000 |
| 6 | 검색광고 | 10000 |
| 7 | SNS | 12000 |
검색광고 주문(1·3·6번)과 SNS 주문(2·5·7번) 가운데 9,000원 이상인 5·6·7번만 남았습니다. 문자열 값은 작은따옴표로 감싸는데요, 숫자는 따옴표 없이 적습니다. 괄호가 왜 필요한지는 바로 아래 섹션에서 괄호를 뺀 결과와 비교해 보겠습니다.
2. IN 과 BETWEEN: 같은 조건을 IN 과 BETWEEN 으로 짧게 쓸 수 있습니다. 이번에는 「검색광고·SNS 주문 중 11월 3일부터 5일까지 들어온 것」입니다.
SELECT order_id, order_date, channel
FROM shop.orders
WHERE channel IN ('검색광고', 'SNS')
AND order_date BETWEEN DATE '2026-11-03' AND DATE '2026-11-05';| order_id | order_date | channel |
|---|---|---|
| 3 | 2026-11-03 | 검색광고 |
| 5 | 2026-11-04 | SNS |
| 6 | 2026-11-05 | 검색광고 |
연산자 문서는 x IN (y, z) 를 (x = y) OR (x = z) 와 같은 뜻으로 정의하고, X BETWEEN Y AND Z 는 Y <= X AND X <= Z 와 같다고 적고 있습니다. 그래서 BETWEEN 은 양 끝 값을 포함하는데요, 11월 3일과 5일 주문이 모두 결과에 들어온 것도 이 때문입니다. 같은 기간의 4번 주문은 이메일이라 IN 조건에서 빠졌습니다.
DATE '2026-11-03' 은 날짜 값을 직접 적는 문법인데요, 변환 규칙 문서에 따르면 문자열 값은 필요할 때 DATE 로 바뀌기 때문에 '2026-11-03' 만 적어도 날짜 열과 비교됩니다. 다만 읽는 사람이 날짜라는 걸 바로 알 수 있게 DATE 를 붙여 두는 편이 낫습니다.
3. LIKE 로 글자 패턴 찾기: 상품명이 「세트」로 끝나는 주문만 찾아보겠습니다. 연산자 문서에 따르면 LIKE 패턴에서 % 는 글자 수와 상관없이 아무 글자, _ 는 아무 글자 하나를 뜻합니다.
SELECT order_id, product
FROM shop.orders
WHERE product LIKE '%세트';| order_id | product |
|---|---|
| 5 | 노트 세트 |
| 7 | 형광펜 세트 |
'%세트' 는 「앞에 무엇이 오든 세트로 끝나는 값」이고, '노트%' 로 바꾸면 노트로 시작하는 2·3·5·8번이 나옵니다. % 나 _ 자체를 찾고 싶을 때는 문서 설명대로 역슬래시 두 개(\\%)를 앞에 붙여야 하는데요, 할인율처럼 % 가 든 값을 찾을 때 헷갈리기 쉬운 부분입니다.
4. IS NULL 로 빈 값 찾기: 쿠폰을 쓰지 않은 주문, 곧 coupon 이 NULL 인 행을 찾습니다. 이때는 = NULL 이 아니라 IS NULL 을 씁니다.
SELECT order_id, coupon
FROM shop.orders
WHERE coupon IS NULL;| order_id | coupon |
|---|---|
| 1 | NULL |
| 3 | NULL |
| 5 | NULL |
| 7 | NULL |
| 8 | NULL |
연산자 문서에 따르면 IS 연산자는 NULL 이 들어와도 TRUE 나 FALSE 만 돌려주고 NULL 을 돌려주지 않습니다. 반대로 쿠폰을 쓴 주문은 IS NOT NULL 로 찾는데요, 2·4·6번 세 행이 나옵니다. 위 결과 표의 값은 모두 예시 표가 작아 손으로 세어 맞춘 값입니다.
AND 와 OR 를 섞을 때 괄호는 왜 필요할까요?
1번 쿼리에서 괄호를 빼면 어떻게 될까요? 연산자 문서의 우선순위 표를 보면 NOT → AND → OR 순서로 먼저 계산하는데요, 문서 예시처럼 x OR y AND z 는 x OR (y AND z) 로 읽힙니다. 괄호를 빼면 쿼리가 「검색광고 주문 전부, 또는 9,000원 이상인 SNS 주문」이라는 뜻으로 바뀌는 셈이죠.
SELECT order_id, channel, amount
FROM shop.orders
WHERE channel = '검색광고' OR channel = 'SNS'
AND amount >= 9000;| order_id | channel | amount |
|---|---|---|
| 1 | 검색광고 | 6000 |
| 3 | 검색광고 | 3000 |
| 5 | SNS | 9000 |
| 6 | 검색광고 | 10000 |
| 7 | SNS | 12000 |
금액 조건이 SNS 에만 붙어서 6,000원·3,000원짜리 검색광고 주문(1·3번)까지 섞여 나왔습니다. 오류 없이 결과가 나오기 때문에 행 수를 세어 보지 않으면 알아채기 어려운데요, 그래서 저는 AND 와 OR 가 한 WHERE 안에 같이 있으면 무조건 괄호를 친다는 습관을 들이려 합니다. 엑셀의 AND·OR 함수가 궁금하시면 엑셀 TRUE FALSE 함수 글도 함께 보시면 좋습니다.
개념 그림: AND 가 OR 보다 먼저 묶이기 때문에 괄호가 없으면 금액 조건이 SNS 에만 붙습니다
지금까지 나온 조건을 한 표에 모으면 이렇습니다. 같은 뜻을 여러 방법으로 쓸 수 있을 때 어떤 쪽이 읽기 쉬운지가 고르는 기준이 됩니다.
| 조건 | 쓰는 법 | 같은 뜻의 다른 표현 | 이럴 때 |
|---|---|---|---|
| 같다·크다·작다 | amount >= 9000 | — | 숫자·날짜·문자 하나와 비교 |
| 여러 값 중 하나 | channel IN ('검색광고', 'SNS') | channel = '검색광고' OR channel = 'SNS' | 후보 값이 셋 이상이면 IN 이 짧다 |
| 범위 | amount BETWEEN 5000 AND 10000 | amount >= 5000 AND amount <= 10000 | 양 끝을 포함하는 범위 |
| 글자 패턴 | product LIKE '%세트' | — | 시작·끝·포함 여부 |
| 빈 값 | coupon IS NULL | — (= NULL 은 안 된다) | 값이 없는 행 찾기 |
NULL 은 왜 조건에서 빠질까요?
4회 데이터 타입과 NULL 글에서 NULL 은 「값을 모른다」는 표시라 3값 논리를 따른다고 했는데요, WHERE 에서 그 규칙이 실제로 결과를 바꿉니다. 연산자 문서의 예시를 보면 NULL = 'a' 는 FALSE 가 아니라 NULL 이 되고, NOT(NULL) 도 다시 NULL 입니다. 그리고 WHERE 는 TRUE 인 행만 남기니 NULL 이 나온 행은 버려지는 거죠.
그래서 「WELCOME 쿠폰을 쓰지 않은 주문」을 이렇게 찾으면 생각과 다른 결과가 나옵니다.
SELECT order_id, coupon
FROM shop.orders
WHERE coupon != 'WELCOME';| order_id | coupon |
|---|---|
| 4 | VIP10 |
쿠폰을 아예 안 쓴 1·3·5·7·8번도 「WELCOME 을 안 쓴 주문」인데 결과에 없습니다. 이 다섯 행은 NULL != 'WELCOME' 이 NULL 이 되어 걸러졌기 때문인데요, NULL 인 행까지 넣으려면 조건을 하나 더 붙여야 합니다.
SELECT order_id, coupon
FROM shop.orders
WHERE coupon != 'WELCOME' OR coupon IS NULL;이번에는 4번에 1·3·5·7·8번이 더해져 6행이 나옵니다. 같은 이유로 NOT IN 목록 안에 NULL 이 섞여 있으면 조심해야 하는데요, 연산자 문서의 규칙대로라면 같은 값을 못 찾았을 때 비교 중에 NULL 이 나오면 결과가 NULL 이라, channel NOT IN ('SNS', NULL) 은 한 행도 돌려주지 않습니다.
개념 그림: NULL 과 비교하면 TRUE 도 FALSE 도 아닌 NULL 이 나오고, WHERE 는 이 행을 버립니다
실무에서는 어떻게 쓸까요? 지난주 주간 보고 쿼리
지난 글에서 만든 주간 보고용 쿼리에 WHERE 를 붙여 보겠습니다. 매주 월요일 「지난주(11월 2일 월요일부터 8일 일요일까지) 검색광고·SNS 주문 중 쿠폰을 안 쓴 것」을 따로 보고하는 담당자라고 가정해 볼게요.
- 기간부터 거릅니다:
order_date BETWEEN DATE '2026-11-02' AND DATE '2026-11-08'로 양 끝을 포함한 한 주를 잡습니다. - 채널을 IN 으로 묶습니다: 채널이 늘어날 수 있으니 OR 를 이어 쓰기보다
IN (...)목록에 추가하는 편이 고치기 쉽습니다. - 빈 값은 IS NULL 로 찾습니다: 쿠폰을 안 쓴 주문은
coupon IS NULL입니다. - 행 수를 손으로 세어 봅니다: 예시 표 8행 중 기간에는 8행이 다 들어오고, 검색광고·SNS 는 6행, 그중 쿠폰이 NULL 인 것은 1·3·5·7번 4행이어야 합니다.
SELECT
order_date,
channel,
product,
amount
FROM shop.orders
WHERE order_date BETWEEN DATE '2026-11-02' AND DATE '2026-11-08'
AND channel IN ('검색광고', 'SNS')
AND coupon IS NULL; -- 쿠폰 미사용조건을 AND 로만 이었기 때문에 이번에는 괄호가 필요 없습니다. 다음 주에는 날짜 두 개만 바꾸면 그대로 다시 쓸 수 있습니다.
하나 더, LIKE 는 대소문자를 구분합니다. BigQuery 콜레이션 문서에 따르면 콜레이션을 따로 지정하지 않은 문자열 비교는 대소문자를 구분해서 coupon LIKE 'vip%' 로는 VIP10 을 찾지 못합니다. 다른 DBMS 에서는 PostgreSQL 패턴 매칭 문서처럼 대소문자를 무시하는 ILIKE 를 따로 두기도 하는데요, 문서에 따르면 이건 표준이 아닌 PostgreSQL 확장 기능입니다.
연습 문제
1. (쉬움) shop.orders 에서 이메일 채널 주문의 주문 번호와 금액을 꺼내는 쿼리를 써 보세요.
정답 보기
SELECT order_id, amount
FROM shop.orders
WHERE channel = '이메일';4번(15000)과 8번(3000) 2행이 나옵니다. 문자열 값 '이메일' 은 작은따옴표로 감쌉니다.
2. (보통) 금액이 5,000원 이상 10,000원 이하이면서 쿠폰을 쓴 주문을 찾는 쿼리를 쓰고, 결과가 몇 행인지 답해 보세요.
정답 보기
SELECT order_id, amount, coupon
FROM shop.orders
WHERE amount BETWEEN 5000 AND 10000
AND coupon IS NOT NULL;금액 조건에는 1(6000)·2(6000)·5(9000)·6(10000)번이 맞고, 그중 쿠폰이 NULL 이 아닌 것은 2번(WELCOME)과 6번(WELCOME)이라 2행입니다. BETWEEN 은 양 끝을 포함해서 10,000원인 6번도 들어갑니다.
3. (보통) 상품명이 「노트」로 시작하고 11월 3일 이후(3일 포함)에 들어온 주문은 몇 건일까요? 쿼리와 함께 답해 보세요.
정답 보기
SELECT order_id, order_date, product
FROM shop.orders
WHERE product LIKE '노트%'
AND order_date >= DATE '2026-11-03';노트로 시작하는 주문은 2·3·5·8번이고, 그중 11월 2일인 2번이 빠져 3·5·8번 3건입니다. product = '노트' 로 쓰면 「노트 세트」인 5번이 빠진다는 점도 비교해 보세요.
자주 하는 실수
= NULL로 빈 값을 찾습니다:coupon = NULL은 모든 행에서 NULL 이 되어 결과가 0행입니다. 오류가 나지 않아서 「쿠폰 안 쓴 주문이 없네」라고 잘못 읽기 쉬운데요, 빈 값은 늘IS NULL,IS NOT NULL로 찾습니다.!=조건을 쓸 때도 NULL 인 행이 빠진다는 점을 함께 떠올리시길 권합니다.- AND 와 OR 를 괄호 없이 섞습니다: AND 가 먼저 묶여서 조건 일부가 엉뚱한 쪽에 붙습니다. 위 예시처럼 3행이 나와야 할 쿼리가 5행을 돌려줘도 오류는 나지 않기 때문에, 두 연산자가 함께 있으면 괄호를 치고 결과 행 수를 한 번 세어 보는 편이 안전합니다.
- WHERE 에서 SELECT 의 별칭을 씁니다:
SELECT amount AS sales ... WHERE sales >= 10000은 오류가 납니다. 쿼리 문법 문서에 따르면 WHERE 는 FROM 절로 들어온 열만 참조할 수 있고 SELECT 목록의 별칭은 쓸 수 없는데요, 5회 실행 순서 글에서 본 것처럼 WHERE 가 SELECT 보다 먼저 계산되기 때문입니다. WHERE 에는 원래 열 이름(amount)이나 계산식을 그대로 적습니다.
마치며
SQL WHERE 절은 조건이 TRUE 인 행만 남기는 필터이고, 비교 연산자·AND·OR·IN·BETWEEN·LIKE·IS NULL 을 조합하면 보고서에 필요한 행만 정확히 골라낼 수 있습니다. AND 와 OR 를 섞을 때는 괄호를, 빈 값이 있을 수 있는 열에는 IS NULL 을 먼저 떠올리시길 권합니다. 저도 시리즈를 따라가며 공부하고 있으니 전체 커리큘럼을 참고해 주세요. 다음 글에서는 ORDER BY 로 결과를 정렬하고 CASE WHEN 으로 금액대를 나누는 법을 다룹니다.
자주 묻는 질문
- SQL WHERE 절은 어떻게 쓰나요?
- FROM 뒤에 WHERE 와 조건식을 적습니다. 예를 들어 SELECT order_id FROM shop.orders WHERE channel = '이메일'; 은 이메일 채널 주문만 돌려줍니다. 조건식이 TRUE 인 행만 남고 FALSE 나 NULL 인 행은 버려집니다.
- SQL BETWEEN 은 양 끝 값을 포함하나요?
- 포함합니다. BigQuery 문서는 X BETWEEN Y AND Z 를 Y 이상이면서 Z 이하인 조건과 같은 뜻으로 정의합니다. 그래서 BETWEEN DATE '2026-11-03' AND DATE '2026-11-05' 는 3일과 5일 주문도 결과에 넣습니다.
- WHERE 에서 NULL 은 어떻게 찾나요?
- = NULL 이 아니라 IS NULL 을 씁니다. NULL 과의 비교는 TRUE 도 FALSE 도 아닌 NULL 이 되어 WHERE 에서 버려지기 때문에, coupon = NULL 은 한 행도 돌려주지 않습니다. 값이 있는 행은 IS NOT NULL 로 찾습니다.
관련 글 더 보기
- SQL SELECT 문 기초: SELECT·FROM 으로 원하는 열만 꺼내기 - SQL 기초 시리즈 6회, 열 고르기와 별칭
- SQL 데이터 타입과 NULL: 숫자·문자·날짜, 그리고 빈 값 - SQL 기초 시리즈 4회, NULL 과 3값 논리
- 엑셀 TRUE FALSE 함수 | AND·OR·NOT 논리 함수 쓰는 법 - WHERE 의 AND·OR 와 닮은 엑셀 논리 함수