모든 강의를 무료로 볼 수 있어요. 회원가입 없이도 학습 가능합니다.

10강 / 전체 24강

GROUP BY

9분 읽기 조회 0

GROUP BY로 데이터를 그룹으로 나누고 각 그룹에 집계 함수를 적용해 카테고리별·등급별·월별 통계를 구합니다.

🎯 학습 목표

이 강을 마치면 GROUP BY를 사용해 데이터를 원하는 기준으로 그룹화하고, 각 그룹에 집계 함수를 적용해 그룹별 통계를 구할 수 있습니다. "카테고리별 도서 수와 평균 가격", "등급별 회원 수", "월별 주문 건수"처럼 데이터 분석의 핵심 패턴을 자유롭게 작성할 수 있게 됩니다. 또한 여러 열로 그룹화하는 다중 GROUP BY도 이해할 수 있습니다.

💡 GROUP BY가 필요한 이유

이 섹션에서는 GROUP BY가 왜 필요한지, 9강의 집계 함수와 어떻게 다른지 살펴보겠습니다.

9강에서 SELECT COUNT(*) FROM books는 전체 도서 수 하나를 반환했습니다. 그런데 "카테고리별로 도서가 몇 권인지" 알고 싶다면 어떻게 해야 할까요? WHERE로 카테고리를 하나씩 필터링하면 쿼리를 세 번 실행해야 합니다.

-- 이런 방법은 비효율적
SELECT COUNT(*) FROM books WHERE category = 'IT';
SELECT COUNT(*) FROM books WHERE category = '경제';
SELECT COUNT(*) FROM books WHERE category = '소설';

GROUP BY는 이 문제를 한 번의 쿼리로 해결합니다. 지정한 열의 값이 같은 행들을 하나의 그룹으로 묶은 뒤, 각 그룹에 집계 함수를 적용합니다.

-- GROUP BY를 사용한 카테고리별 도서 수
SELECT category, COUNT(*) AS 도서수
FROM books
GROUP BY category;

결과는 다음과 같습니다.

category도서수
IT3
경제2
소설2

GROUP BY category가 실행되면 books 테이블의 7개 행이 category 값 기준으로 세 그룹으로 나뉘고, 각 그룹에 COUNT(*)가 적용되어 3개 행의 결과가 반환됩니다. GROUP BY 없이 집계 함수만 쓰면 전체가 하나의 그룹이 되고, GROUP BY를 쓰면 여러 그룹으로 나뉩니다.

📊 GROUP BY 기본 사용법

이 섹션에서는 GROUP BY와 집계 함수를 다양하게 조합하는 방법을 살펴보겠습니다.

GROUP BY는 SELECT 문에서 WHERE 다음, ORDER BY 앞에 위치합니다. 전체 실행 순서는 FROM → WHERE → GROUP BY → SELECT(집계) → ORDER BY → LIMIT입니다.

카테고리별 도서 통계를 여러 집계 함수로 한 번에 구해봅니다.

SELECT
  category           AS 카테고리,
  COUNT(*)           AS 도서수,
  ROUND(AVG(price), 0) AS 평균가격,
  MAX(price)         AS 최고가,
  MIN(price)         AS 최저가,
  SUM(stock)         AS 총재고
FROM books
GROUP BY category
ORDER BY 도서수 DESC;

결과에는 세 카테고리 각각의 통계가 한 행씩 나타납니다. ORDER BY 도서수 DESC는 도서 수가 많은 카테고리부터 정렬합니다. SQLite는 GROUP BY 결과에서 SELECT 별칭을 ORDER BY에 사용할 수 있습니다.

회원을 등급별로 분류해 통계를 내봅니다.

SELECT
  grade              AS 등급,
  COUNT(*)           AS 회원수,
  MIN(join_date)     AS 최초가입일,
  MAX(join_date)     AS 최근가입일
FROM members
GROUP BY grade
ORDER BY 회원수 DESC;

VIP 2명, REGULAR 2명, PREMIUM 1명, NULL(등급 미설정) 1명이 각 행으로 나타납니다. NULL 등급도 하나의 그룹으로 묶입니다.

날짜 열을 strftime으로 변환해 월별 그룹화도 할 수 있습니다.

SELECT
  strftime('%Y-%m', order_date) AS 주문월,
  COUNT(*)                      AS 주문건수,
  SUM(quantity)                 AS 총주문수량
FROM orders
GROUP BY strftime('%Y-%m', order_date)
ORDER BY 주문월 ASC;

2024-03, 2024-04, 2024-05, 2024-06처럼 월별로 주문 건수와 수량이 집계됩니다. GROUP BY에 strftime 함수를 사용하면 날짜 필드의 연도·월 단위 집계가 가능합니다.

🔑 GROUP BY의 핵심 규칙

이 섹션에서는 GROUP BY 사용 시 반드시 지켜야 하는 규칙을 살펴보겠습니다.

GROUP BY를 사용할 때 SELECT에 쓸 수 있는 열에는 규칙이 있습니다. SELECT에는 GROUP BY에 포함된 열 또는 집계 함수만 쓸 수 있습니다.

-- 올바른 예: category는 GROUP BY에 있음, COUNT(*)는 집계 함수
SELECT category, COUNT(*)
FROM books
GROUP BY category;

-- 오류! title은 GROUP BY에 없고 집계 함수도 아님
SELECT category, title, COUNT(*)
FROM books
GROUP BY category;

두 번째 쿼리가 오류인 이유를 생각해봅니다. category로 그룹화하면 IT 그룹에 3개 행이 있습니다. 그런데 SELECT에 title을 요청하면 "IT 그룹의 title은 무엇인가?"라는 질문에 'SQL 첫걸음', '파이썬 독학', '클린 코드' 세 가지 답이 가능합니다. 데이터베이스는 어떤 값을 표시해야 할지 알 수 없어 오류를 냅니다.

이 규칙이 9강에서 언급한 "집계 함수와 일반 열을 함께 쓸 수 없다"는 제한의 근본 원인입니다. GROUP BY 없이 집계 함수를 쓰면 전체가 하나의 그룹이 되는데, 이 하나의 그룹에서 각 행마다 다른 title 값을 어떻게 표시할지 결정할 수 없습니다.

🔀 다중 열 GROUP BY

이 섹션에서는 두 개 이상의 열로 그룹화하는 다중 GROUP BY를 살펴보겠습니다.

GROUP BY에 여러 열을 나열하면 열 조합이 동일한 행끼리 하나의 그룹이 됩니다. 예를 들어 GROUP BY category, status는 "IT + DONE", "IT + PENDING", "경제 + DONE"처럼 두 열의 조합으로 그룹화합니다.

주문 테이블에서 회원별·상태별 주문 수를 구해봅니다.

SELECT
  member_id          AS 회원번호,
  status             AS 주문상태,
  COUNT(*)           AS 주문건수,
  SUM(quantity)      AS 총수량
FROM orders
GROUP BY member_id, status
ORDER BY member_id, status;

각 회원과 주문 상태의 조합이 하나의 행으로 나타납니다. 1번 회원의 DONE 주문, 1번 회원의 PENDING 주문, 2번 회원의 DONE 주문처럼 세분화됩니다.

카테고리별 상태별 도서 통계도 구할 수 있습니다. 단, 주문 테이블(status)과 도서 테이블(category)은 별개이므로 여기서는 books의 카테고리와 stock의 유무(NULL 여부)로 다중 그룹화를 실습해봅니다.

-- 카테고리와 저자의 조합별 통계
SELECT
  category,
  author,
  COUNT(*)   AS 도서수,
  SUM(stock) AS 재고합계
FROM books
GROUP BY category, author
ORDER BY category, author;

같은 카테고리에 저자가 여럿이면 각각 별도 행으로 나타납니다. 한 저자가 같은 카테고리에 여러 권을 썼다면 하나의 행으로 집계됩니다.

다중 GROUP BY를 쓸 때 ORDER BY도 함께 명시해 결과가 의미 있는 순서로 나타나게 하는 것이 좋습니다.

🔗 WHERE + GROUP BY 조합

이 섹션에서는 WHERE로 먼저 행을 필터링한 후 GROUP BY로 집계하는 패턴을 살펴보겠습니다.

WHERE와 GROUP BY를 함께 사용할 때 실행 순서가 중요합니다. WHERE는 GROUP BY보다 먼저 실행되므로, GROUP BY 대상이 되는 행을 먼저 WHERE로 걸러낼 수 있습니다.

-- 2024년 5월 이후 주문만 대상으로 회원별 집계
SELECT
  member_id          AS 회원번호,
  COUNT(*)           AS 주문건수,
  SUM(quantity)      AS 총주문수량
FROM orders
WHERE order_date >= '2024-05-01'
GROUP BY member_id
ORDER BY 주문건수 DESC;

WHERE로 5월 이후 주문만 남긴 다음, 그 주문들을 회원별로 집계합니다. WHERE를 먼저 처리하므로 4월 이전 주문은 집계에서 완전히 제외됩니다.

취소되지 않은 주문만 대상으로 도서별 판매량을 구해봅니다.

SELECT
  book_id            AS 도서번호,
  COUNT(*)           AS 주문건수,
  SUM(quantity)      AS 총판매수량
FROM orders
WHERE status != 'CANCEL'
GROUP BY book_id
ORDER BY 총판매수량 DESC;

취소된 주문을 제외하고 도서별 실제 판매 집계가 나타납니다. 가장 많이 팔린 도서(총판매수량 내림차순)를 파악할 수 있습니다.

WHERE + GROUP BY + ORDER BY의 조합은 실무 보고서 쿼리의 기본 골격입니다. 이 패턴을 완전히 익혀두면 대부분의 통계 요구사항을 처리할 수 있습니다.

📝 핵심 요약

이번 강에서 배운 내용을 정리하겠습니다.

개념문법설명
단일 열 그룹화GROUP BY 열해당 열 값이 같은 행을 하나의 그룹으로 묶음
다중 열 그룹화GROUP BY 열1, 열2열 조합이 같은 행끼리 묶음
SELECT 규칙GROUP BY 열 or 집계 함수만그룹화하지 않은 일반 열은 SELECT에 쓸 수 없음
실행 순서FROM → WHERE → GROUP BY → SELECT → ORDER BYWHERE가 GROUP BY보다 먼저 실행
날짜 그룹화GROUP BY strftime('%Y-%m', 날짜열)월·연도 단위 집계에 활용

GROUP BY는 집계 함수와 결합해 데이터 분석의 핵심 도구로 활용됩니다. 그런데 "카테고리별 평균 가격이 20,000원 이상인 카테고리만 보고 싶다"처럼 집계 결과를 조건으로 거를 때는 WHERE를 쓸 수 없습니다. WHERE는 GROUP BY보다 먼저 실행되기 때문에 집계 결과를 아직 모르기 때문입니다. 다음 11강에서는 이 문제를 해결하는 HAVING을 배웁니다. GROUP BY + HAVING 조합으로 집계 결과에 조건을 걸 수 있게 됩니다.

관련 주제

  • GROUP BY
  • 다중 컬럼 그룹화
  • 카테고리별 통계
  • 그룹별 집계
  • 개발·프로그래밍
  • 개발·프로그래밍 강의
  • SQL·데이터베이스 입문 24강
  • 무료강의
  • 무료 온라인 강의
  • NUGUNA
  • 누구나

📚 시리즈 전체 공유

SQL·데이터베이스 입문 24강

이 강의가 속한 시리즈는 총 24강, 모두 무료입니다. 처음부터 배우려는 동료에게 시리즈 전체를 알려 주세요.

댓글

0/1000

불러오는 중...