개주 훈련일지/📚 코살대 교본 학습

SQL) 그룹 함수

lshfood2 2026. 2. 2. 00:45

[ 그룹 함수 ]

그룹 함수를 이용하여 특정 집합의

소계/중계/합계/총합계를 구할 수 있다.

 

오라클 DBMS에서 제공하는 그룹 함수의 종류

종류 설명
ROLLUP - 소그룹 간의 소계를
계산하는 기능이다.
- ROLLUP 함수 내에 인자로
지정된 그룹화 칼럼은
소계를 생성하는데 사용된다.
- 그룹화 칼럼의 수가
N이라고 했을 때
N+1의 소계가 생성된다.
- ROLLUP 함수 내의
인자 순서가 바뀌면
결과도 바뀌게 된다.(계층 구조)
CUBE - 다차원적인 소계를
계산하는 기능이다.
- 결합 가능한 모든 값에 대하여
다차원 집계를 생성한다.
- CUBE 함수 내에 칼럼이 N개라면
2의 N승만큼의 소계가 생성된다.
- 시스템에 많은 부담을 주기 때문에
사용상 주의가 필요하다.
GROUPING SETS - 특정 항목에 대한
소계를 계산하는 기능이다.

공통 샘플 데이터 (예시 자료)

-- 예시용 매출 데이터 (지역/상품별 매출)
WITH sales AS (
    SELECT DATE '2026-01-02' AS sale_dt, 'SEOUL'  AS region, 'HEADPHONE' AS product, 120000 AS amount FROM dual UNION ALL
    SELECT DATE '2026-01-03' AS sale_dt, 'SEOUL'  AS region, 'SPEAKER'   AS product, 350000 AS amount FROM dual UNION ALL
    SELECT DATE '2026-01-05' AS sale_dt, 'BUSAN'  AS region, 'HEADPHONE' AS product,  90000 AS amount FROM dual UNION ALL
    SELECT DATE '2026-01-06' AS sale_dt, 'BUSAN'  AS region, 'SPEAKER'   AS product, 210000 AS amount FROM dual UNION ALL
    SELECT DATE '2026-01-10' AS sale_dt, 'DAEGU'  AS region, 'SPEAKER'   AS product, 180000 AS amount FROM dual
)
SELECT * FROM sales;

[ ROLLUP ]

ROLLUP은 인자 순서대로 계층형 소계를 만든다.

(region, product)면 상세 합계 다음에

region 소계가 나오고, 마지막에 전체 총계가 나온다.

 

함수 예시 (소계 + 총계)

SELECT
    region,
    product,
    SUM(amount) AS total_amount
FROM sales
-- ROLLUP(region, product)
-- 1) (region, product) 상세 합계
-- 2) (region) 지역별 소계 (product가 NULL로 나옴)
-- 3) () 전체 총계 (region, product 둘 다 NULL)
GROUP BY ROLLUP(region, product)
ORDER BY
    region  NULLS LAST,
    product NULLS LAST;

소계/총계 행에서는 하위 컬럼이 NULL로 표시되므로,

NULL이 합계를 의미한다는 점만 알고 보면 된다.


같은 컬럼이라도 인자 순서를 바꾸면

소계 기준이 바뀌어서 결과가 달라진다.

 

ROLLUP 실행 결과 예시

region  | product    | total_amount
BUSAN   | HEADPHONE  |  90000
BUSAN   | SPEAKER    | 210000
BUSAN   | NULL       | 300000   -- 지역 소계
DAEGU   | SPEAKER    | 180000
DAEGU   | NULL       | 180000   -- 지역 소계
SEOUL   | HEADPHONE  | 120000
SEOUL   | SPEAKER    | 350000
SEOUL   | NULL       | 470000   -- 지역 소계
NULL    | NULL       | 950000   -- 전체 총계

[ ROLLUP + GROUPING ]

ROLLUP 결과에서 NULL이 나오는 행이

원래 데이터의 NULL인지(실데이터)

집계용 NULL인지 헷갈릴 수 있는데,

오라클은 GROUPING(), GROUPING_ID()로

'이 NULL은 집계 때문에 생긴 거다' 를 판별하게 해준다.

 

함수 예시 (소계/총계 행 구분)

SELECT
    -- GROUPING(col) = 1 이면 '그 컬럼이 집계(소계/총계)로 인해 NULL 처리된 상태'
    CASE WHEN GROUPING(region)  = 1 THEN 'ALL_REGION'  ELSE region  END AS region_label,
    CASE WHEN GROUPING(product) = 1 THEN 'ALL_PRODUCT' ELSE product END AS product_label,

    SUM(amount) AS total_amount,

    -- GROUPING_ID(region, product)
    -- 여러 컬럼의 GROUPING 결과를 비트마스크로 묶어 숫자로 보여준다.
    -- 값 해석은 컬럼 나열 순서 기준으로 결정된다(오라클 내부 규칙).
    GROUPING_ID(region, product) AS gid
FROM sales
GROUP BY ROLLUP(region, product)
ORDER BY
    -- 라벨 기준으로 정렬 (총계는 맨 아래로)
    CASE WHEN GROUPING(region)  = 1 THEN 1 ELSE 0 END,
    region_label,
    CASE WHEN GROUPING(product) = 1 THEN 1 ELSE 0 END,
    product_label;

GROUPING(col)=1이면 해당 컬럼의 NULL이

‘집계 때문에 만들어진 NULL’이라는 뜻이다.

 

그래서 소계/총계 행을 ALL_REGION,

ALL_PRODUCT 같은 라벨로 바꾸면 해석이 쉬워진다.

GROUPING_ID(region, product)는 집계 레벨을 숫자로 요약한다.

  • gid=0은 상세
  • gid=1은 지역 소계(상품이 집계됨)
  • gid=3은 전체 총계를 의미

즉 라벨과 gid만 보면 소계/총계 행을 빠르게 구분할 수 있다.

 

ROLLUP + GROUPING, GROUPING_ID 실행 결과 예시

region_label | product_label | total_amount | gid
BUSAN        | HEADPHONE     |  90000       | 0
BUSAN        | SPEAKER       | 210000       | 0
BUSAN        | ALL_PRODUCT   | 300000       | 1   -- 지역 소계
DAEGU        | SPEAKER       | 180000       | 0
DAEGU        | ALL_PRODUCT   | 180000       | 1   -- 지역 소계
SEOUL        | HEADPHONE     | 120000       | 0
SEOUL        | SPEAKER       | 350000       | 0
SEOUL        | ALL_PRODUCT   | 470000       | 1   -- 지역 소계
ALL_REGION   | ALL_PRODUCT   | 950000       | 3   -- 전체 총계

[ CUBE ]

CUBE(region, product)는 아래 조합을 전부 만들어준다.

  • (region, product) 상세
  • (region) 지역 소계
  • (product) 상품 소계
  • () 전체 총계

함수 예시 (모든 조합의 소계/총계)

SELECT
    CASE WHEN GROUPING(region)  = 1 THEN 'ALL_REGION'  ELSE region  END AS region_label,
    CASE WHEN GROUPING(product) = 1 THEN 'ALL_PRODUCT' ELSE product END AS product_label,
    SUM(amount) AS total_amount
FROM sales
GROUP BY CUBE(region, product)
ORDER BY
    region_label,
    product_label;

CUBE는 (region) 소계뿐 아니라 (product) 소계까지

함께 만들어서, 가능한 조합의 소계를 전부 생성한다.


컬럼이 N개면 2의 N승만큼 조합이 늘어나

결과 행이 급증할 수 있으니,

데이터가 크면 사용 범위를 제한하는 게 좋다.

 

CUBE 실행 결과 예시

region_label | product_label | total_amount
ALL_REGION   | ALL_PRODUCT   | 950000   -- 전체 총계
ALL_REGION   | HEADPHONE     | 210000   -- 상품 소계
ALL_REGION   | SPEAKER       | 740000   -- 상품 소계

BUSAN        | ALL_PRODUCT   | 300000   -- 지역 소계
BUSAN        | HEADPHONE     |  90000
BUSAN        | SPEAKER       | 210000

DAEGU        | ALL_PRODUCT   | 180000   -- 지역 소계
DAEGU        | SPEAKER       | 180000

SEOUL        | ALL_PRODUCT   | 470000   -- 지역 소계
SEOUL        | HEADPHONE     | 120000
SEOUL        | SPEAKER       | 350000

[ UNION ALL + GROUP BY ]

ROLLUP(region, product)이 만들어내는 3단계를

UNION ALL로 직접 합쳐서 구현하는 방식이다.

 

함수 예시 (ROLLUP을 수동으로 구현)

-- 1) 상세(지역+상품)
SELECT
    region,
    product,
    SUM(amount) AS total_amount,
    'DETAIL' AS row_type
FROM sales
GROUP BY region, product

UNION ALL

-- 2) 지역 소계
SELECT
    region,
    NULL AS product,
    SUM(amount) AS total_amount,
    'SUBTOTAL_BY_REGION' AS row_type
FROM sales
GROUP BY region

UNION ALL

-- 3) 전체 총계
SELECT
    NULL AS region,
    NULL AS product,
    SUM(amount) AS total_amount,
    'GRAND_TOTAL' AS row_type
FROM sales
ORDER BY
    -- 정렬을 깔끔하게: DETAIL -> SUBTOTAL -> TOTAL
    CASE row_type
        WHEN 'DETAIL' THEN 1
        WHEN 'SUBTOTAL_BY_REGION' THEN 2
        ELSE 3
    END,
    region NULLS LAST,
    product NULLS LAST;

ROLLUP이 만드는 ‘상세/지역 소계/전체 총계’를 각각

GROUP BY로 따로 만든 뒤 UNION ALL로 합친 방식이다.


row_type을 붙이면 출력 정렬이 쉬워지고,

상세만/소계만 필터링하는 것도 간단해진다.

 

UNION ALL + GROUP BY 실행 결과 예시

region  | product    | total_amount | row_type
BUSAN   | HEADPHONE  |  90000       | DETAIL
BUSAN   | SPEAKER    | 210000       | DETAIL
DAEGU   | SPEAKER    | 180000       | DETAIL
SEOUL   | HEADPHONE  | 120000       | DETAIL
SEOUL   | SPEAKER    | 350000       | DETAIL

BUSAN   | NULL       | 300000       | SUBTOTAL_BY_REGION
DAEGU   | NULL       | 180000       | SUBTOTAL_BY_REGION
SEOUL   | NULL       | 470000       | SUBTOTAL_BY_REGION

NULL    | NULL       | 950000       | GRAND_TOTAL

[ GROUPING SETS ]

GROUPING SETS는 어떤 집계 조합을

만들지 명시적으로 적는 방식이다.


CUBE는 가능한 조합 전부, ROLLUP은 계층적 소계,

GROUPING SETS는 내가 고른 조합만 보여준다.

 

아래 예시는 (region, product), (region), (product), ()를

전부 만드는 케이스라 결과적으로 CUBE와 같지만,

필요한 조합만 남기면 훨씬 깔끔해진다.

 

함수 예시 (원하는 조합 합계 데이터 출력)

SELECT
    CASE WHEN GROUPING(region)  = 1 THEN 'ALL_REGION'  ELSE region  END AS region_label,
    CASE WHEN GROUPING(product) = 1 THEN 'ALL_PRODUCT' ELSE product END AS product_label,
    SUM(amount) AS total_amount
FROM sales
GROUP BY GROUPING SETS (
    (region, product), -- 상세
    (region),          -- 지역 소계
    (product),         -- 상품 소계
    ()                 -- 전체 총계
)
ORDER BY
    region_label,
    product_label;

GROUPING SETS는 만들고 싶은 집계 조합만

명시적으로 선택하는 방식이라, 의도가 가장 분명하다.


CUBE처럼 모든 조합을 만들지 않고

필요한 소계만 생성할 수 있어서,

성능과 가독성 측면에서 자주 선택된다.

 

GROUPING SETS 실행 결과 예시

region_label | product_label | total_amount
ALL_REGION   | ALL_PRODUCT   | 950000
ALL_REGION   | HEADPHONE     | 210000
ALL_REGION   | SPEAKER       | 740000
BUSAN        | ALL_PRODUCT   | 300000
BUSAN        | HEADPHONE     |  90000
BUSAN        | SPEAKER       | 210000
DAEGU        | ALL_PRODUCT   | 180000
DAEGU        | SPEAKER       | 180000
SEOUL        | ALL_PRODUCT   | 470000
SEOUL        | HEADPHONE     | 120000
SEOUL        | SPEAKER       | 350000

'개주 훈련일지 > 📚 코살대 교본 학습' 카테고리의 다른 글

컴포넌트 구성 원리  (0) 2026.02.07
객체지향 설계 원리  (1) 2026.02.06
전통적인 설계 원리  (0) 2026.02.01
품질 목표  (0) 2026.01.31
설계 원리와 설계 기본 개념  (0) 2026.01.26