DB/MySql

[MySQL] MySQL 소계쿼리의 모든 것: 데이터 분석의 강력한 도구

shaprimanDev 2025. 5. 11. 20:21
반응형

 

 

데이터 분석에서 합계나 소계를 계산하는 것은 매우 중요한 작업입니다. MySQL에서는 이러한 작업을 수행하기 위한 다양한 방법을 제공하는데, 그 중에서도 소계쿼리(Subtotal Query)는 데이터를 다양한 레벨에서 집계할 수 있는 강력한 도구입니다.

소계쿼리란 무엇인가?

소계쿼리는 데이터를 그룹화하고 각 그룹별로 집계 값을 계산한 다음, 이러한 집계를 다양한 레벨에서 제공하는 쿼리입니다. 예를 들어, 제품별 판매량, 카테고리별 판매량, 그리고 전체 판매량을 한 번의 쿼리로 확인할 수 있습니다.

GROUP BY와 WITH ROLLUP

MySQL에서 소계를 구현하는 가장 기본적인 방법은 GROUP BY절과 함께 WITH ROLLUP 수정자를 사용하는 것입니다.

SELECT 
    category, 
    product, 
    SUM(sales) AS total_sales
FROM 
    sales_data
GROUP BY 
    category, product
WITH ROLLUP;

이 쿼리는 카테고리와 제품별로 판매량을 집계한 후, 추가적으로 다음과 같은 집계 행을 생성합니다:

  • 각 카테고리의 총 판매량 (product 열이 NULL)
  • 전체 판매량 (category와 product 열이 모두 NULL)

GROUPING() 함수

WITH ROLLUP으로 생성된 소계 행에서는 NULL 값이 나타납니다. 이것이 실제 데이터의 NULL 값인지 소계를 위한 NULL 값인지 구분하기 위해 GROUPING() 함수를 사용할 수 있습니다.

SELECT 
    IF(GROUPING(category) = 1, '모든 카테고리', category) AS category,
    IF(GROUPING(product) = 1, '모든 제품', product) AS product,
    SUM(sales) AS total_sales
FROM 
    sales_data
GROUP BY 
    category, product
WITH ROLLUP;

이 쿼리는 소계 행에 '모든 카테고리', '모든 제품'과 같은 레이블을 표시하여 가독성을 높입니다.

TIP: GROUPING() 함수는 MySQL 8.0 이상에서 지원됩니다. 이전 버전에서는 IFNULL()을 사용하여 유사한 결과를 얻을 수 있지만, 실제 NULL 값과 소계용 NULL 값을 구분할 수 없습니다.

UNION을 이용한 소계 구현

WITH ROLLUP이 MySQL 5.0 이상에서 지원되지만, 더 세밀한 제어가 필요하거나 이전 버전을 사용해야 하는 경우 UNION을 활용할 수 있습니다.

-- 제품별 판매량
SELECT category, product, SUM(sales) AS total_sales
FROM sales_data
GROUP BY category, product

UNION ALL

-- 카테고리별 판매량
SELECT category, '모든 제품' AS product, SUM(sales) AS total_sales
FROM sales_data
GROUP BY category

UNION ALL

-- 전체 판매량
SELECT '모든 카테고리' AS category, '모든 제품' AS product, SUM(sales) AS total_sales
FROM sales_data;

이 방식은 코드가 길어지지만, 레이블을 더 명확하게 지정할 수 있고 소계 행의 순서를 더 잘 제어할 수 있습니다.

다중 레벨 소계와 순서 제어

복잡한 분석에서는 여러 레벨의 소계가 필요할 수 있습니다. 이런 경우 ORDER BY 절을 추가하여 결과의 순서를 제어할 수 있습니다.

SELECT 
    IF(GROUPING(year) = 1, '모든 연도', year) AS year,
    IF(GROUPING(quarter) = 1, '모든 분기', quarter) AS quarter,
    IF(GROUPING(month) = 1, '모든 월', month) AS month,
    SUM(sales) AS total_sales
FROM 
    sales_data
GROUP BY 
    year, quarter, month
WITH ROLLUP
ORDER BY 
    year, quarter, month;

이 쿼리는 연도, 분기, 월별로 데이터를 집계하고, 각 레벨에서 소계를 생성합니다.

실무 활용 예시: 판매 데이터 분석

실제 비즈니스 환경에서 소계 쿼리가 어떻게 활용되는지 살펴보겠습니다.

SELECT 
    IF(GROUPING(region) = 1, '전체 지역', region) AS region,
    IF(GROUPING(product_category) = 1, '전체 카테고리', product_category) AS category,
    SUM(sales_amount) AS total_sales,
    ROUND(AVG(sales_amount), 2) AS avg_sales,
    COUNT(DISTINCT customer_id) AS customer_count
FROM 
    sales_transactions
WHERE 
    transaction_date BETWEEN '2024-01-01' AND '2024-03-31'
GROUP BY 
    region, product_category
WITH ROLLUP
ORDER BY 
    GROUPING(region), 
    region, 
    GROUPING(product_category), 
    product_category;

이 쿼리는 지역별, 제품 카테고리별로 매출 합계, 평균 매출, 고객 수를 계산하고 소계를 제공합니다. ORDER BY 절에서 GROUPING() 함수를 사용하여 소계 행이 관련 그룹 바로 아래에 표시되도록 정렬합니다.

피벗 테이블과 소계 결합하기

데이터 분석에서 행과 열을 교차시켜 보여주는 피벗 테이블은 매우 유용합니다. MySQL에서는 CASE 표현식을 사용하여 피벗 테이블을 구현할 수 있으며, 이를 소계 쿼리와 결합할 수 있습니다.

SELECT 
    IF(GROUPING(product_category) = 1, '총계', product_category) AS category,
    SUM(CASE WHEN quarter = 'Q1' THEN sales_amount ELSE 0 END) AS Q1_sales,
    SUM(CASE WHEN quarter = 'Q2' THEN sales_amount ELSE 0 END) AS Q2_sales,
    SUM(CASE WHEN quarter = 'Q3' THEN sales_amount ELSE 0 END) AS Q3_sales,
    SUM(CASE WHEN quarter = 'Q4' THEN sales_amount ELSE 0 END) AS Q4_sales,
    SUM(sales_amount) AS yearly_total
FROM 
    sales_data
WHERE 
    year = 2024
GROUP BY 
    product_category
WITH ROLLUP;

이 쿼리는 제품 카테고리별로 분기별 매출과 연간 총매출을 보여주는 피벗 테이블을 생성하고, 마지막에 전체 합계 행을 추가합니다.

category Q1_sales Q2_sales Q3_sales Q4_sales yearly_total
전자기기 125000 138000 142000 185000 590000
가구 84000 92000 87000 110000 373000
의류 67000 75000 82000 115000 339000
총계 276000 305000 311000 410000 1302000

성능 고려사항

소계 쿼리는 데이터량이 많을 경우 성능에 영향을 줄 수 있습니다. 최적화를 위한 몇 가지 팁은 다음과 같습니다:

  1. 필요한 열만 선택하여 쿼리 처리 속도를 향상시킵니다.
  2. 적절한 인덱스를 사용하여 GROUP BY 연산을 최적화합니다.
  3. 대용량 데이터의 경우, 임시 테이블을 사용하거나 미리 집계된 데이터를 준비할 수 있습니다.
  4. EXPLAIN 명령을 사용하여 쿼리 실행 계획을 분석하고 병목 현상을 파악합니다.
주의: WITH ROLLUP을 사용한 쿼리는 임시 테이블을 생성할 가능성이 높으며, 대용량 데이터에서는 메모리 사용량이 증가할 수 있습니다. 대규모 데이터셋에서는 성능 테스트를 진행하고 필요에 따라 쿼리를 최적화하는 것이 중요합니다.

결론

MySQL의 소계 쿼리는 데이터 분석과 보고서 작성에 필수적인 도구입니다. WITH ROLLUP, GROUPING() 함수, 또는 UNION 접근 방식을 통해 다양한 레벨에서의 집계 정보를 얻을 수 있습니다. 이를 통해 의사 결정에 필요한 다양한 각도에서의 데이터 인사이트를 한 번의 쿼리로 얻을 수 있어, 업무 효율성과 데이터 분석의 품질을 크게 향상시킬 수 있습니다.

추가 학습 자료:
반응형