SQL WHERE와 GROUP BY는 데이터를 걸러내고 묶는 두 축입니다. 이 글은 비개발자가 처음 SQL을 배울 때 헷갈리기 쉬운 두 문법의 차이와 순서, 자주 나오는 오류의 원인을 짧은 예시로 정리합니다.

SQL의 WHERE와 GROUP BY는 데이터를 "골라내는" 문법과 "묶어서 세는" 문법입니다. 둘 다 SELECT 문 안에서 함께 쓰이지만 역할이 다르고, 실행되는 순서도 정해져 있습니다. 이 순서를 모른 채 쓰면 "집계 함수는 여기서 못 쓴다"거나 "GROUP BY에 없는 컬럼이 있다"는 에러가 반복해서 납니다.
비개발자가 SQL을 처음 배울 때 가장 많이 막히는 지점이 바로 여기입니다. 엑셀의 필터·피벗 테이블과 개념은 비슷한데 문법이 훨씬 엄격하거든요. 이 글에서는 두 문법의 차이, 함께 쓸 때의 순서, 그리고 HAVING이라는 세 번째 조각까지 짧은 예시로 짚어봅니다.
먼저 SQL 한 줄이 무슨 뜻인지
SQL(Structured Query Language)은 데이터베이스(정리된 표들의 창고)에게 "이런 데이터를 보여줘"라고 요청하는 언어입니다. 가장 기본 형태는 이렇게 생겼습니다.
SELECT 컬럼이름
FROM 테이블이름
WHERE 조건;
풀어 말하면 "어떤 테이블에서, 어떤 조건을 만족하는 행의, 어떤 컬럼을 보여줘"입니다. 엑셀에 비유하면 SELECT는 "어느 열을 볼지", FROM은 "어느 시트를 볼지", WHERE는 "어느 행만 걸러낼지"에 해당합니다.
예를 들어 orders라는 주문 테이블이 있다고 해봅시다.
| order_id | customer | amount | region |
|---|---|---|---|
| 1 | 김민수 | 30000 | 서울 |
| 2 | 이지은 | 12000 | 부산 |
| 3 | 박현우 | 55000 | 서울 |
| 4 | 김민수 | 8000 | 서울 |
이 표를 계속 예시로 쓰겠습니다.
WHERE: 원하는 행만 걸러내기
WHERE는 행 단위 필터입니다. 조건에 맞는 행만 남기고 나머지는 버립니다. 엑셀의 자동 필터와 역할이 같습니다.
서울 지역 주문만 보고 싶다면 이렇게 씁니다.
SELECT order_id, customer, amount
FROM orders
WHERE region = '서울';
이 쿼리는 region 값이 '서울'인 행 3개만 골라 옵니다. 조건은 여러 개를 이어붙일 수 있습니다.
SELECT *
FROM orders
WHERE region = '서울' AND amount >= 20000;
여기서 AND는 "둘 다 참", OR는 "둘 중 하나만 참이면 통과"라는 뜻입니다. >=는 "크거나 같다"입니다.
자주 쓰는 WHERE 조건 몇 가지를 정리해두면 편합니다.
| 문법 | 뜻 | 예시 |
|---|---|---|
= '값' |
정확히 일치 | region = '서울' |
<> 또는 != |
다르다 | region <> '부산' |
>, <, >=, <= |
크기 비교 | amount > 10000 |
BETWEEN A AND B |
A와 B 사이 | amount BETWEEN 1 AND 50000 |
IN (...) |
목록 중 하나 | region IN ('서울','부산') |
LIKE '김%' |
패턴 검색 | 이름이 '김'으로 시작 |
IS NULL |
값이 비어 있음 | region IS NULL |
💡 문자열은 반드시 작은따옴표(')로 감쌉니다.region = 서울처럼 따옴표를 빠뜨리면 "서울이라는 컬럼을 찾을 수 없다"는 오류가 납니다.
GROUP BY: 같은 값끼리 묶어서 요약하기
GROUP BY는 같은 값을 가진 행들을 한 덩어리로 묶는 문법입니다. 그리고 그 덩어리마다 개수·합계·평균 같은 요약값을 뽑을 때 씁니다. 엑셀의 피벗 테이블과 개념이 같습니다.
지역별로 주문 건수와 총 매출을 알고 싶다고 해봅시다.
SELECT region, COUNT(*) AS 주문수, SUM(amount) AS 총매출
FROM orders
GROUP BY region;
결과는 이렇게 나옵니다.
| region | 주문수 | 총매출 |
|---|---|---|
| 서울 | 3 | 93000 |
| 부산 | 1 | 12000 |
여기서 COUNT(*), SUM(amount), AVG(amount), MAX(amount), MIN(amount) 같은 걸 집계 함수라고 부릅니다. "여러 행을 하나의 숫자로 압축하는 함수"라는 뜻입니다.
AS 주문수는 "이 계산 결과에 '주문수'라는 이름표를 붙여줘"라는 의미입니다. 안 붙이면 컬럼 이름이 COUNT(*) 그대로 나와 알아보기 힘듭니다.
GROUP BY에서 가장 많이 나는 오류
입문자가 거의 예외 없이 부딪히는 규칙이 하나 있습니다.
⚠️ SELECT에 쓰는 컬럼은 GROUP BY에 있거나, 집계 함수로 감싸야 합니다.
예를 들어 아래는 오류가 납니다.
-- ❌ 오류
SELECT region, customer, SUM(amount)
FROM orders
GROUP BY region;
"서울"이라는 그룹 안에는 김민수와 박현우가 섞여 있는데, customer 컬럼에 뭘 보여줘야 할지 SQL이 정할 수 없기 때문입니다. 이럴 땐 customer도 묶는 기준에 넣어야 합니다.
-- ⭕ 정상
SELECT region, customer, SUM(amount)
FROM orders
GROUP BY region, customer;
WHERE와 GROUP BY를 같이 쓸 때의 순서
두 문법을 함께 쓸 때는 반드시 WHERE가 먼저, GROUP BY가 나중입니다. 문법 순서뿐 아니라 실행 순서도 그렇습니다.
전체 데이터 ↓ FROM: 테이블 열기 ↓ WHERE: 행 걸러내기 ↓ GROUP BY: 남은 행을 묶기 ↓ 집계 함수 계산 (COUNT, SUM …) ↓ SELECT: 보여줄 컬럼 정하기 ↓ 결과 출력
"먼저 필요한 행만 골라낸 다음, 그 결과를 그룹으로 묶는다"는 흐름입니다.
서울 지역만 대상으로, 고객별 총 주문액을 보고 싶다면 이렇게 씁니다.
SELECT customer, SUM(amount) AS 총액
FROM orders
WHERE region = '서울'
GROUP BY customer;
먼저 WHERE가 서울 주문 3건만 남기고, 그다음 GROUP BY가 그 3건을 고객별로 묶어 합계를 냅니다. 그 결과 고객별 총액이 한 줄씩 나오게 됩니다.
HAVING: 그룹을 걸러낼 때 쓰는 조건
여기까지 오면 "그럼 그룹으로 묶은 다음에도 조건을 걸고 싶을 땐?"이라는 질문이 나옵니다. 예를 들어 "총 주문액이 일정 금액 이상인 고객만" 보고 싶을 때입니다.
이럴 때 WHERE에 SUM(amount) >= 30000을 넣으면 오류가 납니다. WHERE는 그룹으로 묶기 전에 실행되기 때문에, 아직 SUM 값이 존재하지 않거든요.
이때 쓰는 게 HAVING입니다. HAVING은 "그룹으로 묶은 다음 그 결과에 거는 조건"입니다.
SELECT customer, SUM(amount) AS 총액
FROM orders
WHERE region = '서울'
GROUP BY customer
HAVING SUM(amount) >= 30000;
WHERE와 HAVING의 차이를 표로 비교하면 이렇습니다.
| 구분 | WHERE | HAVING |
|---|---|---|
| 걸러내는 대상 | 개별 행 | 묶인 그룹 |
| 실행 순서 | GROUP BY 앞 | GROUP BY 뒤 |
| 집계 함수 사용 | ❌ 불가 | ⭕ 가능 |
| 예시 조건 | amount > 10000 |
SUM(amount) > 10000 |
기억할 요령은 "개별 데이터에 대한 조건이면 WHERE, 요약된 숫자에 대한 조건이면 HAVING"입니다.
자주 막히는 부분과 확인 순서
입문자가 반복해서 만나는 오류 몇 가지를 미리 알아두면 시간을 아낍니다.
- ☐문자열 따옴표 확인:
WHERE name = 김민수❌ →WHERE name = '김민수'⭕ - ☐컬럼 이름 오타:
custmer처럼 한 글자 틀리면 "unknown column" 오류 - ☐GROUP BY 누락: 집계 함수를 썼는데 나머지 컬럼을 GROUP BY에 안 넣음
- ☐WHERE에 집계 함수 사용:
WHERE SUM(amount) > 100❌ → HAVING으로 옮기기 - ☐NULL 비교:
WHERE region = NULL❌ →WHERE region IS NULL⭕ - ☐쿼리 끝 세미콜론(
;): 도구에 따라 있어야 실행됨
오류 메시지가 나오면 당황하지 말고 메시지 안의 컬럼 이름·줄 번호를 그대로 읽는 게 가장 빠른 해결법입니다. 요즘 데이터베이스는 어디가 문제인지 꽤 친절하게 알려줍니다.
이제 어디에 써보면 좋을까
여기까지 오면 SELECT, WHERE, GROUP BY, HAVING이라는 SQL의 뼈대는 어느 정도 갖춘 셈입니다. 실무에서 만나는 리포트 쿼리의 상당수가 이 네 가지 조합으로 만들어집니다.
바로 다음 단계로 연습해볼 만한 것들을 짧게 남깁니다.
- ORDER BY: 결과를 정렬하기 (예:
ORDER BY 총매출 DESC— 내림차순) - LIMIT: 결과 개수 제한하기 (예:
LIMIT 10— 상위 10개만) - JOIN: 두 개 이상의 테이블 합치기 (고객 테이블 + 주문 테이블)
연습은 되도록 직접 데이터를 넣어보며 하는 편이 빠릅니다. 무료로 브라우저에서 바로 SQL을 돌려볼 수 있는 학습 사이트들이 있으니, 방금 예시로 든 orders 같은 간단한 표를 만들어 WHERE 조건을 바꿔가며 결과가 어떻게 달라지는지 눈으로 확인해보시면 개념이 훨씬 빨리 붙습니다.
문법이 헷갈릴 땐 이 글의 실행 순서 다이어그램을 다시 떠올려보세요. "WHERE는 묶기 전, HAVING은 묶은 후" 이 한 줄만 기억해도 대부분의 오류는 스스로 진단할 수 있습니다.
함께 보면 좋은 글
'개발 & 기술 > 데이터베이스' 카테고리의 다른 글
| SQL JOIN 정리: INNER·LEFT·OUTER를 그림으로 이해하기 (0) | 2026.07.26 |
|---|---|
| Chroma vs Pinecone 비교: 벡터 DB 선택 기준 정리 (0) | 2026.07.23 |
| Supabase 무료 요금제 한계, 어디까지 무료로 쓸 수 있을까 (0) | 2026.07.13 |
| Chroma 실전 예제 — 내 문서 검색 챗봇 30분 만들기 (0) | 2026.06.25 |
| Supabase 5분 입문 — 코드 적게 쓰는 백엔드의 정답일까 (0) | 2026.06.23 |