어제 EMP와 DEPT를 나눈 이유는 부서명 중복을 없애기 위해서였다. 그런데 사원 목록 화면마다 부서명을 보여 줘야 한다면 매번 조인이 필요하다. 이 조인이 실제로 성능 병목이 될 때 반정규화를 검토한다.
반정규화(Denormalization)는 역정규화 라고도 부른다.
목적은 성능이다. 조인 횟수를 줄이거나, 대량 집계를 미리 계산해 두거나, 큰 테이블을 접근 범위에 맞게 쪼갠다.
정규화를 건너뛰는 것이 아니다. 정규화를 먼저 끝낸 뒤 필요한 곳만 되돌리는 작업이다.
단계 하는 일 세부 ① 반정규화 대상 조사 정말 성능 문제가 있는지 찾는다 범위 처리 빈도 조사, 대량의 범위 처리 조사, 통계성 프로세스 조사, 테이블 조인 개수 조사 ② 다른 방법 유도 검토 중복 없이 풀 수 있는지 먼저 본다 뷰 사용, 클러스터링 적용, 인덱스 조정, 응용 애플리케이션에서 처리③ 반정규화 적용 그래도 안 되면 적용한다 테이블·칼럼·관계 반정규화
순서가 문제로 나오면 "조사 → 대안 검토 → 적용"이다. 적용이 먼저 오는 보기는 틀린다.
② 단계의 대안들은 모델에 중복을 만들지 않는다. 그래서 반정규화보다 먼저 검토한다.
대량 데이터를 행 단위로 나누는 파티셔닝도 ② 단계 대안으로 드는 교재가 있다.
구분 기법 언제 병합 1:1 관계 테이블 병합 1:1 관계인 두 테이블을 늘 함께 조회할 때 1:M 관계 테이블 병합 1쪽 정보를 M쪽에 합쳐 조인을 없앨 때 슈퍼/서브타입 테이블 병합 슈퍼타입과 서브타입을 늘 함께 볼 때 분할 수직 분할 칼럼 중 일부만 집중적으로 조회할 때. 칼럼 단위 로 나눈다 수평 분할 행이 특정 범위(월·지역)로만 조회될 때. 행 단위 로 나눈다 추가 중복 테이블 추가 다른 업무나 서버의 테이블을 복제해 둘 때 통계 테이블 추가 SUM·AVG 같은 집계를 미리 계산해 둘 때 이력 테이블 추가 마스터 테이블에 있는 레코드를 이력 테이블에 중복해 둘 때 부분 테이블 추가 이용이 집중되는 칼럼만 따로 모은 테이블을 둘 때
원본 주문 테이블 (칼럼 많음, 행 많음)
[수직 분할 — 칼럼으로 자른다] [수평 분할 — 행으로 자른다]
┌──────────┬──────────────┐ ┌──────────────────────┐
│ 주문번호 │ 주문번호 │ │ 2026-01 주문 행들 │
│ 주문일자 │ 배송메모 │ ├──────────────────────┤
│ 금액 │ 포장요청 │ │ 2026-02 주문 행들 │
│ (자주 │ (가끔 보는 │ ├──────────────────────┤
│ 보는 것)│ 긴 칼럼) │ │ 2026-03 주문 행들 │
└──────────┴──────────────┘ └──────────────────────┘
같은 PK로 1:1 테이블 둘 같은 구조, 행 범위만 다르다
수직 분할을 하면 두 테이블이 같은 주식별자를 공유하는 1:1 관계 가 된다.
수평 분할은 데이터베이스의 파티셔닝 으로 구현하는 경우가 많다.
⚠️ 함정 : "수평"과 "수직"의 방향을 헷갈리기 쉽다. 표를 칼로 자른다고 생각하면 된다. 가로선을 그으면 행이 갈리는 수평 분할 , 세로선을 그으면 칼럼이 갈리는 수직 분할 이다.
부서별 인원과 급여 합계를 여러 화면이 보여 준다. 매번 아래 쿼리를 돌리는 대신 결과를 통계 테이블에 저장해 둘 수 있다.
SELECT D . DEPTNO , D . DNAME , COUNT ( E . EMPNO ) AS EMP_CNT, SUM ( E . SAL ) AS SAL_SUM
FROM DEPT D LEFT OUTER JOIN EMP E ON D . DEPTNO = E . DEPTNO
GROUP BY D . DEPTNO , D . DNAME ORDER BY D . DEPTNO ; 실습장에서 실행하고 고쳐 보기
DEPTNO DNAME EMP_CNT SAL_SUM
---------- -------------- ---------- ----------
10 ACCOUNTING 3 8750
20 RESEARCH 5 10875
30 SALES 6 9400
40 OPERATIONS 0
EMP가 14행이면 이 쿼리는 가볍다. 반정규화가 필요한 것은 원천 데이터가 매우 커서 집계가 실제로 느릴 때다.
통계 테이블은 원천이 바뀔 때마다 갱신 해야 한다. 사원 한 명이 입사하면 EMP와 통계 테이블을 함께 고쳐야 한다.
40번 부서의 SAL_SUM이 비어 있는 것은 사원이 없어 합계가 NULL이기 때문이다. NULL과 집계는 Day 5에서 다룬다.
기법 내용 예 중복 칼럼 추가 조인을 줄이려고 다른 테이블의 칼럼을 복사해 둔다 EMP에 DNAME을 함께 저장 파생 칼럼 추가 계산 결과를 미리 저장해 둔다 주문에 "총 주문금액", 부서에 "사원 수" 이력 테이블 칼럼 추가 이력 테이블에 최신 여부·시작일·종료일 같은 기능 칼럼을 둔다 급여 이력에 최신여부 칼럼 PK에 의한 칼럼 추가 복합 PK 안에 의미가 있는 값을 일반 칼럼으로도 따로 둔다 PK 일부인 날짜를 따로 조회용 칼럼으로 응용시스템 오작동을 위한 칼럼 추가 잘못 처리했을 때 되돌리려고 이전 값을 보관한다 이전주소 칼럼
중복 칼럼은 조인 감소 , 파생 칼럼은 계산 감소 가 목적이다. 같은 "칼럼 추가"라도 목적이 다르다.
파생 칼럼의 위험은 원천과 결과가 어긋나는 것이다. 사원이 부서를 옮겼는데 "사원 수"를 갱신하지 않으면 값이 틀어진다.
테이블이나 칼럼은 그대로 두고 관계만 하나 더 맺는 방법이다.
정규화 상태 중복 관계 추가 후
고객 ──< 주문 ──< 주문상품 고객 ──< 주문 ──< 주문상품
│ ▲
└──────────────────┘
(고객번호를 주문상품에 직접 둔다)
주문상품에서 고객 정보를 보려면 원래 주문을 거쳐야 한다. 주문상품에 고객번호를 FK로 직접 두면 조인 경로가 짧아진다 .
교재는 이것을 중복 관계 추가 라고 부른다. 테이블과 칼럼 반정규화처럼 무결성을 해칠 수 있다는 점은 같다.
얻는 것 잃는 것 조인·집계 감소로 조회 성능 향상 중복 값을 함께 고쳐야 하므로 무결성 훼손 위험 SQL이 단순해진다 입력·수정·삭제 시 처리할 곳이 늘어 DML 성능이 떨어질 수 있다 저장 공간이 늘어난다
중복 값을 맞추는 수단으로 트리거, 애플리케이션 로직, 배치 갱신을 쓴다.
결론적으로 반정규화는 "조회가 압도적으로 많고, 다른 방법으로 풀리지 않을 때" 쓰는 마지막 단계다.
내일은 관계가 SQL의 조인 조건으로 바뀌는 과정과, 모델에 트랜잭션 단위를 표현하는 법을 다룬다.
반정규화 : 성능을 위해 정규화된 모델에 의도적으로 중복을 허용하는 기법. 역정규화라고도 한다.
무결성 : 데이터가 서로 모순 없이 정확한 상태. 반정규화가 깨뜨릴 수 있다.
수직 분할 : 칼럼 단위로 테이블을 나누는 것. 결과 테이블끼리 1:1 관계가 된다.
수평 분할 : 행 단위로 테이블을 나누는 것. 파티셔닝으로 구현하는 경우가 많다.
통계 테이블 : 집계 결과를 미리 계산해 저장한 테이블.
파생 칼럼 : 다른 칼럼으로 계산할 수 있는 값을 미리 저장한 칼럼.
중복 관계 : 이미 다른 경로로 이어진 엔터티 사이에 조인 경로를 줄이려고 추가한 관계.
클러스터링 : 자주 함께 읽는 데이터를 물리적으로 가까이 저장하는 방법. 반정규화 전 검토 대상이다.
📝 연습 문제 선택지를 클릭하면 정답·해설이 펼쳐집니다.
반정규화 절차를 순서대로 나열한 것은?
A 반정규화 대상 조사 → 다른 방법 유도 검토 → 반정규화 적용 B 다른 방법 유도 검토 → 반정규화 대상 조사 → 반정규화 적용 C 반정규화 적용 → 반정규화 대상 조사 → 다른 방법 유도 검토 D 반정규화 대상 조사 → 반정규화 적용 → 다른 방법 유도 검토 반정규화를 적용하기 전에 검토하는 "다른 방법"에 해당하지 않는 것은?
A 뷰(View) 사용 B 인덱스 조정 C 중복 칼럼 추가 D 클러스터링 적용 주문 테이블이 매우 커서 월 단위로만 조회된다. 이에 맞춰 같은 구조의 테이블을 월별 행 범위로 나누었다. 이 기법은?
A 수직 분할 B 수평 분할 C 1:1 관계 테이블 병합 D 부분 테이블 추가 칼럼 반정규화 기법에 해당하지 않는 것은?
A 중복 칼럼 추가 B 파생 칼럼 추가 C 이력 테이블 칼럼 추가 D 통계 테이블 추가 반정규화에 대한 설명으로 가장 적절하지 않은 것은?
A 조회 성능을 높이려고 중복을 허용한다 B 반정규화를 하면 데이터 무결성이 강화된다 C 정규화를 마친 모델을 대상으로 적용한다 D 중복된 값을 맞추려고 트리거나 애플리케이션 로직을 쓸 수 있다