본문 바로가기

DB

테이블 비정규화를 통한 가독성 향상과 성능 향상

일반적으로 테이블을 생성할 때는 정규화를 한다. 중복된 데이터를 제거하여 저장 공간을 최소화 하고

데이터의 일관성을 유지하기 위해서이다. 또한 데이터 업데이트시에도 하나의 테이블에서만 하면 되므로

작업효율적이기 때문이다.

 

하지만 실무를 진행하다 보면 많은 테이블들이 생성되어 많은 테이블들과의 조인이 일어나 성능이 저하되는

경우도 있고 쿼리 작성 및 가독성이 떨어지는 경우도 생긴다.

 

이럴경우 뷰 테이블 생성을 고려하기도 한다. 많은 조인이 걸려있는 경우 뷰 테이블을 이용하여 쿼리의 가독성을

향상시킬 수 있기 때문이다. 하지만 조회성능 향상에 대해서는 상황에 따라 다를 수 있다고 알고 있다.

 

그래서 성능 향상 및 가독성을 위해 비정규화를 고려하는 경우가 생긴다.

 

최근 고객사에서 조회 속도에 대한 개선요청건이 들어왔고, 결과적으로는 중복데이터를 생성하는 비정규화로

속도를 향상시킨 경우가 생겼다.

 

무분별한 비정규화로 무결성이 깨지는 경우가 발생해서는 안되겠지만, 테이블간의 종속관계가 깨지지 않게

안정성을 잘 따져가며 비정규화 기법을 이용한다면 작업자의 효율도 향상되고 성능도 향상시킬 수 있다고 생각한다.

 

간단한 예시를 통해  중복컬럼을 통한 비정규화 예시를 살펴보겠다.

 

(https://dataonair.or.kr/db-tech-reference/d-guide/sql/?pageid=5&mod=document&uid=333 를 참조하였습니다)

 

테이블들

CREATE TABLE 공급자
(
    공급자번호 INT PRIMARY KEY,
    공급자명  VARCHAR(20)
);
CREATE TABLE 전화번호
(
    순번    INT,
    공급자번호 INT,
    전화번호  VARCHAR(20),
    PRIMARY KEY (순번, 공급자번호),
    FOREIGN KEY (공급자번호) REFERENCES 공급자 (공급자번호)
);
CREATE TABLE 메일주소
(
    순번    INT,
    공급자번호 INT,
    메일주소  VARCHAR(50),
    PRIMARY KEY (순번, 공급자번호),
    FOREIGN KEY (공급자번호) REFERENCES 공급자 (공급자번호)
);
CREATE TABLE 위치
(
    순번    INT,
    공급자번호 INT,
    위치    VARCHAR(100),
    PRIMARY KEY (순번, 공급자번호),
    FOREIGN KEY (공급자번호) REFERENCES 공급자 (공급자번호)
);

 

 

테이블 다이어그램

테이블 관계

 

다음 테이블들은 '공급자' 테이블 하위에 '전화번호', '메일주소', '위치' 테이블이 관리되는 형태이다.

이 테이블들에서 다음과 같은 요구를 받았다고 하자.

 

   ??? : "공급자 번호 1001~1005에 해당하는 공급자번호, 공급자명, 전화번호, 메일주소, 위치 정보를 조회해 주세요."

 

공급자와 전화번호, 메일주소, 위치는 1:M 관계이고 한 명의 공급자당 여러 개의 전화번호, 메일주소, 위치 정보가 존재한다. 따라서 가장 최근에 변경된 값을 가져오기 위해서는 조금 복잡한 조인이 발생될 수 밖에 없다.

 

SELECT A.공급자명, B.전화번호, C.메일주소, D.위치
FROM 공급자 A
         JOIN (SELECT X.공급자번호, X.전화번호
               FROM 전화번호 X
                        JOIN (SELECT 공급자번호, MAX(순번) AS 순번
                              FROM 전화번호
                              WHERE 공급자번호 BETWEEN '1001' AND '1005'
                              GROUP BY 공급자번호) Y ON X.공급자번호 = Y.공급자번호 AND X.순번 = Y.순번) B ON A.공급자번호 = B.공급자번호
         JOIN (SELECT X.공급자번호, X.메일주소
               FROM 메일주소 X
                        JOIN (SELECT 공급자번호, MAX(순번) AS 순번
                              FROM 메일주소
                              WHERE 공급자번호 BETWEEN '1001' AND '1005'
                              GROUP BY 공급자번호) Y ON X.공급자번호 = Y.공급자번호 AND X.순번 = Y.순번) C ON A.공급자번호 = C.공급자번호
         JOIN (SELECT X.공급자번호, X.위치
               FROM 위치 X
                        JOIN (SELECT 공급자번호, MAX(순번) AS 순번
                              FROM 위치
                              WHERE 공급자번호 BETWEEN '1001' AND '1005'
                              GROUP BY 공급자번호) Y ON X.공급자번호 = Y.공급자번호 AND X.순번 = Y.순번) D ON A.공급자번호 = D.공급자번호
WHERE A.공급자번호 BETWEEN '1001' AND '1005';

 

만약 테이블의 수가 더 많다면, 더 길고 가독성이 낮은 쿼리가 작성될 수 밖에 없을 것이다. 이러한 요인들은

몇백줄 이상의 쿼리를 심심치 않게 마주하게 되는 이유 중 하나일 것이다.

 

이런경우 만약 공급자 테이블을 적절하게 반정규화를 한다면 어떨까?

 

반정규화한 '공급자' 테이블

 

하위 테이블들의 컬럼들을 공급자에 중복하여 추가하였다.

이렇게 반정규화를 했다면 같은 결과를 얻어내기 위한 쿼리는 다음과 같아진다

 

SELECT 공급자명, 전화번호, 메일주소, 위치
FROM 공급자
WHERE 공급자번호 BETWEEN '1001' AND '1005';
 

SQL문이 크게 단순해 져서 쿼리 작성 및 가독성이 향상되었다.

 

이러한 장점들이 있지만 무분별하게 반정규화를 진행한다면 데이터 무결성 유지가 힘들어 질 수 있다.

또한 중복데이터들을 입력, 수정, 삭제할 때는 성능이 낮아질 수 밖에 없다.

따라서 목표에 따라 적절하게 비정규화 수준을 선택하는 것이 중요하다고 생각한다.

 

'DB' 카테고리의 다른 글

같은 SELECT인데 결과가 0건(MyBatis)  (0) 2025.03.30
Index Skip Scan과 In-List 튜닝  (0) 2025.02.16
My SQL 최적화 가이드 - 1. 최적화  (1) 2024.11.28
MySQL) OPTIMIZE TABLE Statement  (0) 2024.09.20
쿼리 성능 최적화(EXISTS, JOIN)  (0) 2024.08.20