본문 바로가기

Oracle

Oracle GROUP BY 활용 가이드

GROUP BY 절은 테이블을 지정한 컬럼 기준으로 그룹화한 뒤, 그룹별 통계 정보를 계산할 때 사용합니다.
본 포스트에서는 GROUP BY의 기본 개념부터 다양한 실전 예제, HAVING 절 활용, 실행 순서까지 단계별로 정리했습니다.


1. GROUP BY 기본 개념

GROUP BY 절은 테이블의 여러 행(row)을 지정한 컬럼 기준으로 묶어(group),
각 그룹별로 집계함수(COUNT, SUM, AVG, MAX, MIN 등)를 적용할 수 있도록 합니다.


2. GROUP BY에 들어갈 수 있는 항목

  • 컬럼명
  • 상수
  • 집계 함수(통계함수)

예를 들어, GROUP BY deptno, '2025년도', COUNT(*) 처럼
고정 문자열이나 집계함수 결과도 그룹핑 키로 사용할 수 있습니다.


3. 실습 예제

3-1. 부서별 인원수·급여 통계

SELECT
  TO_CHAR(SYSDATE, 'YYYY') || '년도'    AS 년도,
  deptno                                AS 부서번호,
  COUNT(*)                              AS 인원수,
  SUM(sapay)                            AS 급여합계,
  FLOOR(AVG(sapay))                     AS 급여평균,
  MAX(sapay)                            AS 최고급여액,
  MIN(sapay)                            AS 최저급여액
FROM sawon
GROUP BY deptno
ORDER BY deptno;

설명

  • TO_CHAR(SYSDATE,'YYYY')||'년도'로 조회 시점을 년도 레이블로 출력
  • GROUP BY deptno로 부서별로 묶은 뒤, 집계함수로 통계값 계산

3-2. 부서별 성별 인원수

SELECT
  TO_CHAR(SYSDATE, 'YYYY') || '년도' AS 년도,
  deptno                             AS 부서번호,
  sasex                              AS 성별,
  COUNT(*)                           AS 인원수
FROM sawon
GROUP BY deptno, sasex
ORDER BY deptno, sasex;

3-3. 입사 연도별 인원수

SELECT
  hire_year   AS 입사년도,
  COUNT(*)    AS 인원수
FROM sawon_view2
GROUP BY hire_year
ORDER BY hire_year;

3-4. 입사 월별 인원수

SELECT
  hire_month  AS 입사월,
  COUNT(*)    AS 인원수
FROM sawon_view2
GROUP BY hire_month
ORDER BY hire_month;

3-5. 입사 계절별 인원수

SELECT
  hire_season AS 입사계절,
  COUNT(*)    AS 인원수
FROM sawon_view2
GROUP BY hire_season
ORDER BY hire_season;

3-6. 입사 연대별 인원수

SELECT
  (FLOOR(hire_year/10) || '0년대') AS 입사연대,
  COUNT(*)                         AS 인원수
FROM sawon_view2
GROUP BY FLOOR(hire_year/10)
ORDER BY FLOOR(hire_year/10);

설명

  • FLOOR(hire_year/10) 연산 결과를 그룹 키로 사용
  • '0년대' 라벨을 붙여 가독성 향상

3-7. 고객뷰 성별 인원수

SELECT
  gogender  AS 성별,
  COUNT(*)  AS 인원수
FROM gogek_view2
GROUP BY gogender
ORDER BY gogender;

3-8. 고객뷰 지역(광역시)별 인원수

SELECT
  SUBSTR(goaddr,1,2) AS 지역코드,
  COUNT(*)           AS 인원수
FROM gogek_view2
GROUP BY SUBSTR(goaddr,1,2)
ORDER BY SUBSTR(goaddr,1,2);

3-9. 고객뷰 연령대별 인원수

SELECT
  (FLOOR(goage/10) || '0대') AS 연령대,
  COUNT(*)                  AS 인원수
FROM gogek_view3
GROUP BY FLOOR(goage/10)
ORDER BY FLOOR(goage/10);

3-10. 사원뷰 성씨별 인원수

SELECT
  SUBSTR(saname,1,1) AS 성씨,
  COUNT(*)           AS 인원수
FROM sawon_view2
GROUP BY SUBSTR(saname,1,1)
ORDER BY SUBSTR(saname,1,1);

4. HAVING 절로 그룹 필터링

  • WHERE 절은 그룹화 이전(개별 행)에 조건을 걸지만,
  • HAVING 절은 그룹화 이후(집계 결과)에 조건을 걸 수 있습니다.
SELECT
  deptno,
  COUNT(*) AS 직원수
FROM sawon_view2
WHERE deptno != 100      -- ① 그룹화 전 필터
GROUP BY deptno          -- ③ 그룹화
HAVING COUNT(*) >= 5     -- ④ 그룹화 후 필터
ORDER BY deptno;         -- ⑥ 정렬

5. 실행 순서 이해하기

		select
		  deptno,count(*)		--	⑤
		from sawon_view2	--	①
		where deptno !=100	--	②	
		group by deptno		--	③	
		having count(*)>=5	--	④	
		order by deptno		--	⑥

6. 팁 & 주의사항

  • NULL 값은 COUNT(col)에서 제외됩니다. 전체 행수는 COUNT(*) 사용
  • 평균(AVG) 소수점 자릿수 조정: ROUND(AVG(col), 2)
  • 대규모 데이터셋: 인덱스·파티셔닝을 통한 집계 성능 최적화
  • 너무 복잡한 그룹화 로직은 뷰(View)로 분리해 재사용성 확보