🐨CoalaCoding
DocsExamplesTry itBoardB반
🐨CoalaCoding

개발자를 위한 한국어 웹 기술 문서

문서

  • JavaScript
  • Web Publishing
  • React
  • Python

커뮤니티

  • 게시판
  • 예제 모음
  • Try it 에디터

기타

  • GitHub
  • 관리자
© 2026 CoalaCoding. All rights reserved.
  • 01_sqlite3로 SQL 명령문 실행
  • 02_DB데이터 일괄추가하기
  • 03_SQLite3 라이브러리를 활용한 SQL 명령문 실행
  • 04_Pandas를 활용한 SQL 명령문 실행
  • 05_CREATE 명령어를 활용한 DB 테이블의 생성
  • 06_DDL 명령어의 이해 및 실습
  • 07_SQL SELECT 명령어의 이해 및 실습
  • 08_SQL INSERT, UPDATE, DELETE 명령어 실습
  • 09_SQL SELECT 세부 명령어 실습 1
  • 10_SQL SELECT 세부 명령어 실습 2
  • 11_SQL INNER JOIN 실습
  • 12_SQL LEFT JOIN 실습
  • 13_SQL GROUP BY 실습 1
  • 14_SQL GROUP BY 실습 2
  • 15_SQL SubQuery 활용 실습
  • 16_데이터셋 기반 DB 구축 & DB 활용 실습 — 리액트 연동 완성
  1. 홈
  2. 문서
  3. Backend
  4. Database & SQL
  5. 14_SQL GROUP BY 실습 2

14_SQL GROUP BY 실습 2

코드 블록의 Try it Yourself 버튼으로 직접 실행할 수 있다.

구문

1. 인트로

GROUP BY의 심화 내용으로, 날짜/시간 데이터를 활용한 그룹화와 복잡한 GROUP BY 심화 패턴을 익힌다. 실제 서비스에서 "월별 매출", "시간대별 주문 수" 같은 통계는 이 패턴으로 만들어진다.


2. 실습 DB 준비

import sqlite3
import pandas as pd
from datetime import datetime, timedelta
import random

conn = sqlite3.connect("orders.db")
cur  = conn.cursor()

cur.execute("""
    CREATE TABLE IF NOT EXISTS sales (
        id       INTEGER PRIMARY KEY AUTOINCREMENT,
        product  TEXT    NOT NULL,
        category TEXT    NOT NULL,
        amount   INTEGER NOT NULL,
        qty      INTEGER DEFAULT 1,
        sale_dt  TEXT    NOT NULL
    )
""")

# 6개월치 샘플 데이터 생성
products = [
    ("갤럭시S24", "스마트폰", 1250000),
    ("아이폰15",  "스마트폰", 1350000),
    ("맥북에어",  "노트북",   1500000),
    ("갤럭시북",  "노트북",   1200000),
    ("아이패드",  "태블릿",   900000),
    ("갤럭시탭",  "태블릿",   700000),
]

random.seed(42)
start = datetime(2024, 7, 1)
for _ in range(200):
    p    = random.choice(products)
    days = random.randint(0, 183)
    dt   = (start + timedelta(days=days)).strftime("%Y-%m-%d")
    cur.execute(
        "INSERT INTO sales (product,category,amount,qty,sale_dt) VALUES (?,?,?,?,?)",
        (p[0], p[1], p[2], random.randint(1, 3), dt)
    )

conn.commit()
print("200개 판매 데이터 생성 완료")

코드 설명:

  • 3행: datetime, timedelta로 날짜 계산을 한다.
  • 20~27행: 6가지 상품 정보를 리스트에 담는다.
  • 29행: random.seed(42) — 동일한 랜덤 데이터를 재현 가능하게 고정한다.
  • 31행: random.choice(products) — 상품 목록에서 무작위로 하나를 선택한다.
  • 33~35행: timedelta(days=days) — 시작일로부터 랜덤 일수를 더하여 판매 날짜를 만든다.

3. 시간/날짜 데이터를 활용한 GROUP BY 실습

3.1. 월별 매출 집계

df = pd.read_sql("""
    SELECT SUBSTR(sale_dt, 1, 7)   AS 월,
           COUNT(id)               AS 주문수,
           SUM(amount * qty)       AS 총매출
    FROM sales
    GROUP BY SUBSTR(sale_dt, 1, 7)
    ORDER BY 월
""", conn)
print("=== 월별 매출 ===")
print(df)

코드 설명:

  • 2행: SUBSTR(sale_dt, 1, 7) — 날짜 문자열에서 앞 7자리(YYYY-MM)만 추출한다.
    • '2024-08-15'에서 '2024-08'을 추출한다.
  • 3행: COUNT(id) — 해당 월의 주문 수를 센다.
  • 4행: SUM(amount * qty) — 금액 × 수량으로 총 매출을 계산한다.
  • 6행: GROUP BY에도 같은 SUBSTR 표현식을 사용한다.

3.2. 카테고리별 월별 매출 (교차 분석)

df = pd.read_sql("""
    SELECT SUBSTR(sale_dt, 1, 7) AS 월,
           category              AS 카테고리,
           SUM(amount * qty)     AS 매출
    FROM sales
    GROUP BY SUBSTR(sale_dt, 1, 7), category
    ORDER BY 월, 카테고리
""", conn)
print("=== 카테고리별 월별 매출 ===")
print(df)

코드 설명:

  • 6행: GROUP BY 월, category — 월과 카테고리 두 기준으로 동시에 그룹화한다.
  • 결과는 각 월의 각 카테고리별 매출을 보여준다.

4. GROUP BY 심화 실습 1

4.1. 파이썬으로 결과 피벗(Pivot(피벗) 변환)

# 월별 × 카테고리별 교차표 만들기
df = pd.read_sql("""
    SELECT SUBSTR(sale_dt, 1, 7) AS 월,
           category              AS 카테고리,
           SUM(amount * qty)     AS 매출
    FROM sales
    GROUP BY SUBSTR(sale_dt, 1, 7), category
""", conn)

pivot = df.pivot(index="월", columns="카테고리", values="매출").fillna(0)
pivot = pivot.astype(int)
print("=== 피벗 테이블 ===")
print(pivot)

코드 설명:

  • 9행: pivot() — 행(index)은 월, 열(columns)은 카테고리, 값은 매출로 교차표를 만든다.
  • 9행: .fillna(0) — 데이터가 없는 셀은 0으로 채운다.
  • 10행: .astype(int) — 소수점을 제거하고 정수로 변환한다.

5. GROUP BY 심화 실습 2

5.1. 상품별 누적 판매 순위

df = pd.read_sql("""
    SELECT product             AS 상품명,
           category            AS 카테고리,
           SUM(qty)            AS 총판매량,
           SUM(amount * qty)   AS 총매출,
           ROUND(AVG(qty), 1)  AS 평균주문수량
    FROM sales
    GROUP BY product, category
    ORDER BY 총매출 DESC
""", conn)
print("=== 상품별 판매 순위 ===")
print(df)

5.2. 전체 대비 카테고리 비중 계산

df = pd.read_sql("""
    SELECT category AS 카테고리,
           SUM(amount * qty) AS 매출,
           ROUND(
               SUM(amount * qty) * 100.0 /
               (SELECT SUM(amount * qty) FROM sales),
               1
           ) AS 비중
    FROM sales
    GROUP BY category
    ORDER BY 매출 DESC
""", conn)
print("=== 카테고리별 매출 비중 ===")
print(df)

코드 설명:

  • 5~7행: 서브쿼리(SubQuery(서브쿼리))로 전체 총 매출을 구하고, 각 카테고리 매출을 나누어 비중을 계산한다.
  • * 100.0 — 정수 나눗셈을 방지하기 위해 100.0을 곱한다.

6. 활용 Plus — 리액트 차트(Chart(차트)) 연동

리액트 대시보드에서 막대 차트(Bar Chart(바 차트)) 데이터를 API로 제공하는 예시이다.

def get_monthly_chart():
    df = pd.read_sql("""
        SELECT SUBSTR(sale_dt, 1, 7) AS month,
               SUM(amount * qty)     AS revenue
        FROM sales
        GROUP BY SUBSTR(sale_dt, 1, 7)
        ORDER BY month
    """, conn)
    return {
        "labels": df["month"].tolist(),
        "data"  : df["revenue"].tolist()
    }

result = get_monthly_chart()
print(result)

코드 설명:

  • 9행: df["month"].tolist() — 월 목록을 리스트로 변환한다. 리액트 차트의 x축 라벨이 된다.
  • 10행: df["revenue"].tolist() — 매출 목록을 리스트로 변환한다. 리액트 차트의 y축 데이터가 된다.
  • 리액트에서 이 데이터를 받아 Chart.js(차트제이에스)나 Recharts(리차트)로 그래프를 그린다.

7. 문제풀기

목차

  • 구문