AI 시대 DB 모델링 & SQL 완벽 마스터3-4 GROUP BY — “~별” 사고법
테크타니 LEARN — 섹션 03 · SQL 핵심 문법

"30대가 몇 명이야?" — 묶어서 세는 SQL,
GROUP BY 와 "~별" 사고법

지금까지는 행을 한 줄씩 꺼내 봤다면, 오늘부터는 묶어서 셉니다. "카테고리별 상품 수", "성별 평균 나이", "회원별 주문 수" — 한국어에 "~별"이 들어가면 거의 100% GROUP BY 입니다. 비개발자가 실무에서 가장 많이 쓰게 될 SQL이 바로 이 강의에서 나옵니다.

난이도보통소요12분 (영상 5–6분 + 실습)준비물3-3 완료 (ORDER BY / LIMIT)실습 DBshop — members · products · orders

이번 섹션의 여정

3-1 ~ 3-3

완료 ✓

3-4 GROUP BY

지금 여기

3-5 HAVING

그룹 조건

3-6 ~ 3-8

JOIN · 뻥튀기 함정 ⭐ · CASE

섹션 4 — DB 모델링

강의 메인

행 단위에서 그룹 단위로 — 진짜 분석의 시작

지금까지의 질문은 전부 행 단위였습니다 — “30대 회원 보여줘” 하면 행이 줄줄이 나왔죠. 그런데 실무에서 진짜 자주 듣는 질문은 결이 다릅니다.

지금까지 — 행 단위 질문

”30대 회원 보여줘”

조건에 맞는 행 목록이 답입니다. SELECT + WHERE 로 충분했어요 — 3-1 ~ 3-2에서 끝낸 이야기.

오늘부터 — 그룹 단위 질문

”30대가 몇 명이야?”

행 목록이 아니라 묶어서 계산한 숫자가 답입니다. “카테고리별 평균 가격은?”, “회원별 주문 수는?” — 전부 GROUP BY 의 영역이에요.

SELECT 문의 뼈대에서 오늘 채우는 칸은 여기입니다.

SELECT 문의 뼈대 — 오늘 채우는 한 칸
SELECT 무엇을 (+ 집계 함수) ← 3-1 + 오늘
FROM 어디서 ← 3-1
WHERE 어떤 행만 ← 3-2
GROUP BY 무엇별로 묶어서 ← 오늘 ⭐
ORDER BY 어떤 순서로 ← 3-3
LIMIT 몇 개만 ← 3-3
시작 전 체크
DBeaver 실행 + MySQL 연결
shop DB 사용 중 (USE shop; 또는 Set as Default)
3-3 ORDER BY / LIMIT 까지 따라옴

집계 함수 5종 — 이름 그대로입니다

묶기 전에, 계산하는 도구부터. 딱 다섯 개입니다.

함수의미
COUNT(*)행 개수
SUM(컬럼)합계
AVG(컬럼)평균
MIN(컬럼)최솟값
MAX(컬럼)최댓값

count, sum, average, minimum, maximum — 정말 이름 그대로예요. 바로 쳐봅시다.

SELECT
  COUNT(*)
FROM
  members;
 
SELECT
  AVG(age),
  MIN(age),
  MAX(age)
FROM
  members;
DBeaver — SQL Editor (Ctrl + Enter)
SELECT COUNT(*) FROM members;
COUNT(*)
20
DBeaver — SQL Editor (Ctrl + Enter)
SELECT AVG(age), MIN(age), MAX(age) FROM members;
AVG(age)MIN(age)MAX(age)
32.90002150
회원 20명 — 평균 32.9살, 최연소 21살, 최고령 50살

행 20줄이 아니라 계산된 숫자 한 줄이 나왔죠. 이게 집계입니다.

COUNT(*) 의 별표

COUNT(*)*“NULL 포함, 모든 행을 세라” 는 뜻입니다. 가장 자주 쓰는 형태이니 이대로 손에 익혀두세요.

GROUP BY — “~별” 사고법 ⭐

이제 오늘의 핵심. 집계 함수에 GROUP BY 를 붙이면, 전체가 아니라 그룹마다 계산합니다.

SELECT
  category_id,
  COUNT(*) AS 상품수
FROM
  products
GROUP BY
  category_id;
DBeaver — 카테고리별 상품 수
SELECT category_id, COUNT(*) AS 상품수 FROM products GROUP BY category_id;
category_id상품수
18
27
36
45
54
5 rows — "카테고리별로 상품이 몇 개?" 의 답

상품 30개가 카테고리 5묶음으로 접히면서, 묶음마다 COUNT(*) 가 한 번씩 돌았습니다. 그리고 여기서 이 강의 전체의 핵심 한 줄 —

⭐ 오늘 단 하나만 기억한다면

한국어에 “~별” 이 보이면 → GROUP BY. “성별 평균 나이”, “날짜별 주문 수”, “카테고리별 매출” — 전부 같은 패턴입니다. 거의 100% 입니다.

한국어 질문SQL 번역
평균 나이는?GROUP BY gender + AVG(age)
날짜 주문 수는?GROUP BY ordered_at + COUNT(*)
카테고리 매출은?GROUP BY category_id + SUM(...)

번역 연습 두 개만 직접 쳐봅시다.

-- 성별 평균 나이
SELECT
  gender,
  AVG(age) AS 평균나이
FROM
  members
GROUP BY
  gender;
 
-- 회원별 주문 수
SELECT
  member_id,
  COUNT(*) AS 주문수
FROM
  orders
GROUP BY
  member_id;
DBeaver — 성별 평균 나이
SELECT gender, AVG(age) AS 평균나이 FROM members GROUP BY gender;
gender평균나이
M34.2000
F31.6000
2 rows — 남성 회원이 평균 2.6살 많네요
회원별 주문 수에는 16명만 나옵니다

회원은 20명인데 결과는 16행이에요. orders 테이블만 보고 묶었으니, 주문이 한 건도 없는 회원 4명은 아예 등장하지 않습니다. 고장이 아니라 당연한 동작이고 — 이 4명까지 포함해 세는 방법(LEFT JOIN)은 3-6에서 배웁니다.

세 줄짜리 쿼리지만

이게 데이터 분석의 70% 입니다. 회사에서 오가는 “~별로 얼마나?” 류의 질문 대부분이, 방금 친 GROUP BY 한 덩어리로 끝나요.

GROUP BY 의 규칙 — SELECT 절 제한 ⚠️

여기서 함정 하나. 그대로 따라 쳐보세요 — 일부러 에러를 내볼 겁니다.

SELECT
  member_id,
  ordered_at,
  COUNT(*)
FROM
  orders
GROUP BY
  member_id;
DBeaver — 에러가 나는 게 정상입니다
SELECT
member_id,
ordered_at,
COUNT(*)
FROM
orders
GROUP BY
member_id;
 
SQL Error [1055]: Expression #2 of SELECT list is not in
GROUP BY clause and contains nonaggregated column
'shop.orders.ordered_at' … sql_mode=only_full_group_by

규칙은 이겁니다 — SELECT 절에는 GROUP BY 에 적은 컬럼 + 집계 함수만 쓸 수 있어요. ordered_at 은 GROUP BY 에도 없고 집계 함수도 아닌데 SELECT 에 있어서 에러가 난 거죠.

생각해보면 당연합니다. “회원별로 묶었는데, 그 그룹의 주문일이 뭐냐” — 김철수 한 그룹 안에 주문일이 6개나 들어 있는데, DB 더러 “하나만 골라줘” 라고 할 수는 없으니까요.

✕ 에러 — 묶이지 않은 컬럼

GROUP BY 에 없는 컬럼을 SELECT

한 그룹 안에 값이 여러 개일 수 있어서 모호함 → 에러. MySQL 이 Error 1055 로 거부합니다.

✓ 해결 — 둘 중 하나

GROUP BY 에 넣거나, 집계로 감싸거나

보고 싶은 컬럼을 GROUP BY 에 같이 넣거나, MAX(ordered_at) 처럼 집계 함수로 감싸서 그룹당 값이 하나가 되게 만듭니다.

예를 들어 “회원별 주문 수에 이름도 같이 보고 싶다” 면, 이름을 GROUP BY 에 같이 넣으면 됩니다 (회원ID 와 이름은 1:1 이라 안전).

SELECT
  m.member_id,
  m.member_name,
  COUNT(*) AS 주문수
FROM
  orders o
JOIN members m
  ON o.member_id = m.member_id
GROUP BY
  m.member_id,
  m.member_name;
JOIN 은 3-6에서 — 지금은 규칙만

두 테이블을 붙이는 JOIN 은 3-6에서 제대로 배웁니다. 지금은 “GROUP BY 에 적은 컬럼 외엔 SELECT 못 한다” 한 줄만 외워두세요.

🤖 AI 검증 포인트

AI 도 자주 틀리는 지점입니다. AI 가 만들어준 SQL 에 GROUP BY 가 보이면, SELECT 절에 GROUP BY 에 없는 컬럼이 끼어 있지 않은지 한 번 점검하세요 — 그대로 돌리면 Error 1055 가 납니다.

”TOP N” 패턴 — 3-1 ~ 3-4 가 합쳐지는 순간

GROUP BY 결과에 3-3에서 배운 ORDER BY 를 붙이면, 진짜 “분석 보고서” 가 됩니다.

-- 카테고리별 상품 수 — 많은 순으로
SELECT
  category_id,
  COUNT(*) AS 상품수
FROM
  products
GROUP BY
  category_id
ORDER BY
  상품수 DESC;
별칭 그대로 정렬 가능

ORDER BY 상품수 DESCSELECT 에서 붙인 별칭(AS 상품수)을 ORDER BY 에 그대로 쓸 수 있습니다. 편해요.

여기에 LIMIT 까지 — “가장 주문 많이 한 회원 TOP 5”, 일명 VIP 고객 보고서입니다.

SELECT
  member_id,
  COUNT(*) AS 주문수
FROM
  orders
GROUP BY
  member_id
ORDER BY
  주문수 DESC
LIMIT 5;
DBeaver — VIP 고객 TOP 5
SELECT member_id, COUNT(*) AS 주문수 FROM orders GROUP BY member_id ORDER BY 주문수 DESC LIMIT 5;
member_id주문수
16
24
94
33
43
5 rows — 1번 회원(김철수)이 6건으로 압도적 1위
3건짜리 동률, 기억나시죠?

주문 3건인 회원이 4명(3·4·6·12번)이라 마지막 두 자리는 실행할 때마다 다른 회원이 나올 수 있습니다. 3-3에서 배운 그대로 — 동률의 순서는 보장되지 않아요.

🏆 이게 진짜 보고서

SELECT(3-1) + GROUP BY(3-4) + ORDER BY + LIMIT(3-3). 지금까지 배운 게 전부 들어간 한 쿼리입니다. “TOP N” 패턴 — 실무 보고서의 표준 답안이에요.

한눈에 정리

키워드의미예시
COUNT(*)행 개수 (NULL 포함)SELECT COUNT(*) FROM members;
SUM(c)합계SELECT SUM(price) FROM products;
AVG(c)평균SELECT AVG(age) FROM members;
MIN(c) MAX(c)최솟값 · 최댓값SELECT MIN(price), MAX(price) FROM products;
GROUP BY c”c별” 묶기SELECT c, COUNT(*) FROM t GROUP BY c;
규칙SELECT 에는 GROUP BY 컬럼 + 집계만어기면 Error 1055
GROUP BY + ORDER BY + LIMIT”TOP N” 패턴VIP 고객 TOP 5
✓ 오늘의 핵심 한 줄

“~별” 단어가 보이면 → GROUP BY. 이 한 줄만 몸에 새겨도, 데이터 분석 질문의 대부분이 SQL 로 번역됩니다.

자주 막히는 지점

① SQL Error 1055 — only_full_group_by 가 떠요

오늘 배운 바로 그 규칙 위반입니다. MySQL 기본 설정(only_full_group_by)이 모호한 쿼리를 막아주는 거예요 — 에러가 나는 게 정상이고, 고마운 일입니다.

FIX ▸그 컬럼을 GROUP BY 에 추가하거나, MAX() 같은 집계 함수로 감싸세요. SELECT 절에 GROUP BY 에 없는 컬럼이 끼어 있다는 신호입니다.
② GROUP BY 결과 순서가 뒤죽박죽이에요

GROUP BY 는 묶기만 할 뿐 정렬을 보장하지 않습니다. 순서가 중요하면 ORDER BY 는 항상 직접 붙이는 습관을 들이세요.

FIX ▸ORDER BY 를 명시하세요 — ORDER BY 상품수 DESC 처럼 별칭도 됩니다.
③ WHERE COUNT(*) > 3 이라고 썼더니 에러가 나요

WHERE 는 묶기 전의 행에 거는 조건이라, 집계 함수를 넣을 수 없습니다. “주문 3건 넘는 회원만” 처럼 그룹에 거는 조건은 따로 있어요.

FIX ▸집계 결과에 조건을 걸 때는 WHERE 가 아니라 HAVING — 바로 다음 강의 3-5의 주제입니다.

“~별” 이 보이면 GROUP BY — 오늘은 이 한 줄이면 충분합니다. 연습 문제로 손에 익히고 나면, 다음 3-5에서는 방금 에러 났던 그 질문 — “상품이 5개 이상인 카테고리만 보고 싶다” 처럼 그룹에 조건을 거는 HAVING 으로 넘어갑니다.

NEXT LESSON3-4 연습 문제

카테고리별 평균 가격, VIP 고객 TOP 5 — 방금 배운 '~별' 사고법을 직접 쳐보며 굳힙니다.

GO ▸ 연습 문제 풀기