정규화

유동현·2022년 11월 14일
0

오라클

목록 보기
14/18
post-thumbnail
--○ 정규화란?
--   개발만 하는 개발자에게는 중요하지 않지만,
--   데이터베이스부터 설계해야 하는 개발자에게는 100% 중요함!!!!

-- 왜 테이블을 쪼개고 분리해? (→ 이게 정규화)
-- DB 서버 메모리의 낭비를 막기 위해서

-- 자바에서, 하나의 커다란 클래스보다 작은 여러개의 클래스들이 위력적이듯이
-- DB에서도, 하나의 커다란 테이블보다 작은 여러개의 테이블들이 위력적이다.


-- 한 마디로 데이터베이스 서버의 메모리 낭비를 막기 위해
-- 어떤 하나의 테이블을... 식별자를 가지는 여러 개의 테이블로
-- 나누는 과정을 말한다.


-- ex) 호석이가... 옥장판을 판매한다.
--     고객 리스트 → 거래처 직원 명단이 적혀있는 수첩의 정보를
--                   데이터베이스화 하려고 한다.

-- 테이블명 : 거래처직원
/*
 각 컬럼당 10Byte 차지한다고 가정하자
 10Byte       10Byte    10Byte         10Byte      10Byte  10Byte  10Byte
--------------------------------------------------------------------------------
거래처회사명  회사주소   회사전화       거래처직원명 직급 이메일     휴대폰
--------------------------------------------------------------------------------
    LG       서울여의도  02-345-6789   양윤정       부장 yyj@na... 010-1234-1...
    LG       서울여의도  02-345-6789   최선하       과장 csh@da... 010-2345-2...
    LG       서울여의도  02-345-6789   최문정       대리 cmj@da... 010-3456-3...
    LG       서울여의도  02-345-6789   홍은혜       부장 heh@gm... 010-5678-5...
    SK       서울소공동  02-987-6543   박현수       부장 phs@na... 010-8585-8...
    LG       부산동래구  051-5511-5511 오이삭       대리 oys@te... 010-9900-9...
    SK       서울소공동  02-987-6543   정은정       대리 jej@na... 010-2450-8...
                                        :
                                        :
--------------------------------------------------------------------------------
--> 한 레코드 당 70 Byte
정규화 미흡하면, 가깝게는 파이널 프로젝트 때, 좋은 결과물 만들어내기 어려울 것이다.
만들더라도, DB 설계 과정에서 굉장히 많은 시간이 걸릴 것이다.
멀게는 실무적으로 프로젝트 진행할 때 어려움을 겪는다.
가정) 서울 여의도 LG 라는 회사에 근무하는 거래처 직원 명단이
      총 100만 명이라고 가정한다.
      (한 행(레코드)은 70 Byte 이다.)
      
      어느 날... 『서울여의도』에 위치한 LG 본사가 『경기분당』으로
      사옥을 이전하게 되었다.
      회사 주소는 『경기분당』으로 바뀌고,
      회사 전화는 『031-1111-2222』로 바뀌게 되었다.
      
      그러면... 100만 명의 회사 주소와 회사 전화를 변경해야 한다.
      
      -- 이 때 수행되어야 할 쿼리문 → UPDATE
      
      UPDATE 거래처직원
      SET 회사주소 = '경기분당', 회사전화 = '031-1111-2222'
      WHERE 거래처회사명 = 'LG'
        AND 회사주소 = '서울 여의도';
        
      -- 100만 개 행을 하드디스크상에서 읽어다가
         메모리에 로드시켜 주어야 한다.
         즉, 100만 * 70Byte 를 모두 
         하드디스크상에서 읽어다가 메모리에 로드시켜 주어야 한다는 말이다.
         
         --> 이는 테이블의 설계가 잘못되었으므로 
             DB 서버는 조만간 메모리 고갈로 인해 다운될 것이다.
             
             --> 그러므로 정규화 과정을 수행하여 
                 테이블의 잘못된 설계를 바로잡아야 한다.
*/

-- 제 1 정규화
--> 어떤 하나의 테이블에 반복되는 컬럼 값들이 존재한다면
--  값들이 반복되어 나오는 컬럼을 분리하여
--  새로운 테이블을 만들어준다.

--> 책: 릴레이션에 속한 모든 속성의 도메인이 원자 값으로만 구성

--> 제 1 정규화를 수행하는 과정에서 분리된 테이블은
--  반드시 부모 테이블과 자식 테이블의 관계를 갖게 된다.

--> 부모 테이블 → 참조받는 컬럼 → PRIMARY KEY
--  자식 테이블 → 참조하는 컬럼 → FOREIGN KEY

--※ 참조받는 컬럼이 갖는 특징
--   ① 반드시 고유한 값(데이터)만 들어와야 한다.
--      중복된 값(데이터)이 없어야 한다.
--   ② NULL 이 있어서는 안된다. (NOT NULL 이어야 한다. 비어있어서는 안된다.)

--> 제 1정규화를 수행하는 과정에서
--  부모 테이블의 PRIMARY KEY 는 항상 자식 테이블의 FOREIGN KEY 로 전이된다.



-- 거래처직원 테이블을 아래와 같이 2개로 쪼갬
/*
-- 일단 쪼갠 지금 현재 두 테이블 모두 100만 건 이상의 데이터가 있었지만,
-- 테이블명 : 회사
 10Byte       10Byte    10Byte        10Byte 
--------------------------------------------------
  회사ID   거래처회사명   회사주소   회사전화       
--------------------------------------------------
    10          LG       서울여의도  02-345-6789   
    10          LG       서울여의도  02-345-6789   
    10          LG       서울여의도  02-345-6789   
    10          LG       서울여의도  02-345-6789  
    20          SK       서울소공동  02-987-6543   
    30          LG       부산동래구  051-5511-5511 
    30          SK       서울소공동  02-987-6543                                                                              
---------------------------------------------------
-- ① 변경사항
-- 테이블이 분리함으로 인해서,
-- 회사 테이블에서는, 
-- LG 서울 여의도~ / SK 서울 소공동~ 등 반복되는 건 없애고
-- 아래와 같이 다 한 건씩만 남겨두면 된다.
-- 회사 테이블에는 100만 건이 아니라 3건의 데이터만 있으면 된다.
-- 테이블명 : 직원
 10Byte      10Byte  10Byte     10Byte
------------------------------------------------
 거래처직원명   직급  이메일      휴대폰
------------------------------------------------
   양윤정       부장  yyj@na...  010-1234-1...
   최선하       과장  csh@da...  010-2345-2...
   최문정       대리  cmj@da...  010-3456-3...
   홍은혜       부장  heh@gm...  010-5678-5...
   박현수       부장  phs@na...  010-8585-8...
   오이삭       대리  oys@te...  010-9900-9...
   정은정       대리  jej@na...  010-2450-8...
------------------------------------------------
--② 이 테이블만 보고는 직원이 어디서 근무하는 지 알수가 없음
--   → 그래서 『식별자』를 갖게 아래와 같이 잘라야 한다.
--      여기서의 식별자 : 회사ID
    ↓ 변경 후
    
-- 테이블명 : 회사  → 부모 테이블  
 10Byte       10Byte    10Byte        10Byte 
--------------------------------------------------
  회사ID   거래처회사명   회사주소   회사전화  
  ======
  참조받는 컬럼 → P.K(PRIMARY KEY)
--------------------------------------------------
    10          LG       서울여의도  02-345-6789   
    20          SK       서울소공동  02-987-6543   
    30          LG       부산동래구  051-5511-5511                                                                            
---------------------------------------------------    
    
-- 테이블명 : 직원  → 자식 테이블     
     10Byte   10Byte  10Byte     10Byte           10Byte
-----------------------------------------------------------
 거래처직원명   직급  이메일      휴대폰           회사ID
                                                 -------
                                                 참조하는 컬럼 → F.K(FOREIGN KEY)
-----------------------------------------------------------
   양윤정       부장  yyj@na...  010-1234-1...       10
   최선하       과장  csh@da...  010-2345-2...       10
   최문정       대리  cmj@da...  010-3456-3...       10
   홍은혜       부장  heh@gm...  010-5678-5...       10
   박현수       부장  phs@na...  010-8585-8...       20
   오이삭       대리  oys@te...  010-9900-9...       30
   정은정       대리  jej@na...  010-2450-8...       20
            :
            :
-----------------------------------------------------------
-- 제약조건 : ORACLE이 문법적으로 강제하는 것
-- ex) P.K 제약조건, F.K 제약조건
       직원테이블의 회사ID에는 10번이 또 들어와도 상관없지만,
       회사테이블의 회사ID에는 10번이 또 들어오면 안됨
       회사테이블의 회사ID가 비어 있다면, 직원테이블에서 참조할 수가 없음
       ------------------
       → 고유한 값이 있어야 함, 비어있으면 안 됨
       
*/

-- 분리하고 나니까 원래형태로 볼 수가 없어! 라고 하면 정규화가 제대로 된 게 아님

/*
-- 테이블이 분리(분할)되기 이전 상태로 조회
SELECT A.거래처회사명, A.회사주소, A.회사전화
     , B.거래처직원명, B.직급, B.이메일, B.휴대폰
FROM 회사 A, 직원 B
WHERE A.회사ID = B.회사ID
--윗부분에 썼던 『가정』가져와서 현재에 맞게 필요없는 부분 (X) 표시함
가정) 서울 여의도 LG 라는 회사에 근무하는 거래처 직원 명단이
      총 100만 명이라고 가정한다.
      (한 행(레코드)은 70 Byte 이다.) -- (X) 테이블 나눠놔서
      어느 날... 『서울여의도』에 위치한 LG 본사가 『경기분당』으로
      사옥을 이전하게 되었다.
      회사 주소는 『경기분당』으로 바뀌고,
      회사 전화는 『031-1111-2222』로 바뀌게 되었다.
      
      그러면... 100만 명의 회사 주소와 회사전화를 변경해야 한다. (X)
      회사 테이블에서 1건의 회사주소와 회사전화를 변경해야 한다. (O)
      
      -- 이 때 수행되어야 할 쿼리문 → UPDATE
      
      (X)
      UPDATE 거래처직원
      SET 회사주소 = '경기분당', 회사전화 = '031-1111-2222'
      WHERE 거래처회사명 = 'LG'
        AND 회사주소 = '서울 여의도';
    
      (O)
      UPDATE 회사
      SET 회사주소 = '경기분당', 회사전화 = '031-1111-2222'
      WHERE 회사 ID = 10;
        
        
      (X)  
      -- 100만 개 행을 하드디스크상에서 읽어다가 메모리에 로드시켜 주어야 한다.
         즉, 100만 * 70Byte 를 모두 
         하드디스크상에서 읽어다가 메모리에 로드시켜 주어야 한다는 말이다.
         
      (O)   
      -- 1 개 행을 하드디스크상에서 읽어다가 메모리에 로드시켜 주어야 한다.
         즉, 1 * 40Byte 를  
         하드디스크상에서 읽어다가 메모리에 로드시켜 주어야 한다는 말이다.
         
         --> 정규화 이전에는 100만 건을 처리해야 할 업무에서
             1 건만 처리하면 되는 업무로 바뀐 상황이기 때문에
             DB 서버는 메모리 고갈이 일어나지 않고 아주 빠르게 처리될 것이다.
*/
/*
-- [의문점]
-- 원래는 컬럼 7개의 하나의 테이블이었는데,
-- 식별자를 갖게끔 나누다 보니까
-- 회사 테이블과 직원 테이블에 『회사ID』 라는 컬럼이 생김
-- 원래 하나의 테이블이었을 때는 한 행당 70Byte 였는데,
-- 현재는 회사 테이블은 40Byte, 직원테이블은 50Byte    → 총 90Byte
-- 이게 무슨 영향을 미치지 않을까?
-- ex) 
-- 조회 시, 사용 용량을 살펴보자
-- 거래처회사명, 회사전화 조회한다.
-- 정규화 이전,
SELECT 거래처회사명, 회사전화
FROM 거래처직원;
--> WHERE 절 없이 스캔하므로, 거래처직원 테이블 전체 FULL SCAN 함
--  여의도직원만 100만명이라고 했으니, 테이블 전체는 200만개 레코드 갖고 있다고 하자
--  거래처직원 테이블 총 200만개 레코드 갖고 있음
--  얘는 총 200만 * 70 Byte 사용...
-- 정규화 이후,
SELECT 거래처회사명, 회사전화
FROM 회사;
--> WHERE 절 없이 스캔하므로, 회사 테이블 전체 FULL SCAN 함
--  회사 테이블 총 3개 레코드 갖고 있음
--  얘는 총 3 * 40 Byte = 120 Byte 사용
-- 거래처직원명, 직급 조회한다.
-- 정규화 이전,
SELECT 거래처직원명, 직급
FROM 거래처직원;
--> WHERE 절 없이 스캔하므로, 거래처직원 테이블 전체 FULL SCAN 함
--  거래처직원 테이블 총 200만개 레코드 갖고 있음
--  얘는 총 200만 * 70 Byte 사용...
-- 정규화 이후,
SELECT 거래처직원명, 직급
FROM 직원;
--> WHERE 절 없이 스캔하므로, 직원 테이블 전체 FULL SCAN 함
--  직원 테이블은 200만개 레코드 갖고 있음
--  얘는 총 200만 * 50 Byte = 120 Byte 사용
    (회사테이블)  (직원테이블)
-- 거래처회사명, 거래처직원명 조회한다.
-- 정규화 이전,
SELECT 거래처회사명, 거래처직원명
FROM 거래처직원;
--> WHERE 절 없이 스캔하므로, 거래처직원 테이블 전체 FULL SCAN 함
--  거래처직원 테이블 총 200만개 레코드 갖고 있음
--  얘는 총 200만 * 70 Byte 사용...
-- 정규화 이후,
SELECT A.거래처회사명, B.거래처직원명
FROM 회사 A JOIN 직원 B
ON A.회사ID = B.회사ID;
--> WHERE 절 없이 스캔하므로, 회사, 직원 테이블 전체 FULL SCAN 함
--  (3 * 40Byte) + (200만 * 50Byte)
    ------------    ---------------
     회사테이블        직원테이블
     full scan         full scan
-- 제 2 정규화
*/
-- 테이블명 : 주문
/*
--------------------------------------------------------------------------------
  고객ID             제품코드             주문일자             주문수량
--------------------------------------------------------------------------------
HEH1217(홍은혜)    P-KKBK(꿀꽈배기)    2022-03-01 13:50:23     10
CMJ8335(최문정)    P-KKDS(쿠크다스)    2022-03-01 14:15:11     24
LHS3235(이호석)    P-CCPI(초코파이)    2022-03-01 16:14:36     12
PHS5834(박현수)    P-MGRT(마가레트)    2022-03-02 10:20:54     20
LSM1124(임소민)    P-KSTD(카스타드)    2022-03-02 11:32:47     30
                                :
--------------------------------------------------------------------------------
--PRIMARY KEY 제약조건 설정해야 하는데, 어떤 거에 PK 제약조건 걸어야할까?
고객ID를 PRIMARY KEY로 하면 홍은혜는 한 번 주문하면 다시 주문할 수 없다.
제품코드를 PRIMARY KEY로 하면 꿀꽈배기 한 번 팔면 다시는 팔 수 없다.
주문일자가 PK로 설정되어 있으면 같은 주문일자에는 여러사람이 주문 불가함
주문수량을 PK로 설정되어 있으면 은혜에게 10개 팔면 다른 사람에게 10개 못 팜
고객ID + 제품코드 를 PK로 설정하면, 은혜에게 꿀꽈배기 다시 못 팜
                                   문정이에게 쿠크다스 다시 못 팜
                                   
고객ID + 제품코드 + 주문일자 → 이 구조에서는 이게 가장 바람직함
같은 고객이 같은 제품을 같은 시간대에 주문하겠다고 하면 
ex) 은혜가 같은 시간대에 꿀꽈배기 10개 산다는 주문을 2건 넣으면
    그냥 꿀꽈배기 20건으로 레코드 하나 만들고, 10개짜리 하나 지우면 되니까
*/

--※ 하나의 테이블에 존재하는 PRIMARY KEY 의 최대 개수는 1개이다.
--   하지만, 
--   PRIMARY KEY 를 이루는(구성하는) 컬럼의 개수는 복수(여러개)인 것이 가능하다.
--   컬럼 1개로만 구성된 PRIMARY KEY 를 SINGLE PRIMARY KEY 라고 부른다.
--   (단일 프라이머리 키)
--   두 개 이상의 컬럼으로 구성된 PRIMARY KEY 를 COMPOSITE PRIMARY KEY 라고 부른다.
--   (복합 프라이머리 키)



-- 제 2 정규화
--> 제 1 정규화를 마친 결과물에서 PRIMARY KEY 가 SINGLE COLUMN 이라면
--  제 2 정규화는 수행하지 않는다.
--  하지만, PRIMARY KEY 가 COMPOSITE COLUMN 이라면 
--  반.드.시. 제 2 정규화를 수행해야 한다. (예외없음)

--> 식별자가 아닌 컬럼은 식별자 전체 컬럼에 대해 의존적이어야 하는데
--  식별자 전체 컬럼이 아닌 일부 식별자 컬럼에 대해서만 의존적이라면
--  이를 분리하여 새로운 테이블을 생성해준다.
--  이 과정을 제 2 정규화라 한다.

--> 책: 릴레이션이 제 1 정규형에 속하고,
--      기본키가 아닌 모든 속성이 기본키에 완전 함수 종속

/*
-- 테이블명 : 과목 → 부모 테이블
--------------------------------------------------------------------------------
과목번호   과목명     교수번호    교수자명   강의실코드   강의실설명
++++++++             +++++++++ 
    P.K
--------------------------------------------------------------------------------
 J0101    자바기초     21        슈바이처     A301       전산실습관 3층 40석 규모
 J0102    자바중급     22        테슬라       T502       전자공학관 5층 60석 규모
 O1123    오라클중급   22        테슬라       A201       전산실습관 2층 30석 규모
 O1150    오라클심화   10        장영실       T502       전자공학관 5층 60석 규모
 J3342    JSP응용      20        맥스웰       K101       인문과학관 1층 90석 규모
                                    :
--------------------------------------------------------------------------------
-- 과목 테이블에서 식별자인 컬럼 : 과목번호, 교수번호
-- 그런데, 과목 테이블에서 
-- 과목번호가 바뀌면, 과목명은 따라바뀌지만
-- 교수번호가 바뀐다고, 과목명 신경 1도 안씀
-- 과목명은 과목번호에만 의존적이다.
--> 식별자가 아닌 컬럼은 식별자 전체에 대해 의존적이어야 하는데
    둘 다 한테 의존적인 게 아니라, 일부 식별자한테만 의존적이라면
    이를 분리하여 새로운 테이블을 만들어준다.
--> 과목번호 과목명 |  교수번호 교수자명 강의실코드 강의실설명
--  이렇게 분리한다.
-- 테이블명 : 점수 → 자식 테이블
-----------------------------------------------
과목번호    교수번호    학번              점수
===================
    F.K
++++++++               +++++
    P.K
-----------------------------------------------
O1123       22          2212316(우수정)   80   
O1123       22          2212318(김정용)   92   
O1123       22          2212319(양윤정)   76   
O1123       22          2212323(한충희)   88   
                            :
J0101       21          2212316(우수정)   99  
                            :
-----------------------------------------------
Q. 위 테이블들이 1 정규화를 마친거라고 하면, 어떤게 부모 테이블?
A. 과목 테이블 → 부모 테이블
   점수 테이블 → 자식 테이블
Q. 부모 테이블의 PK는 반드시 자식 테이블의 FK로 전이된다고 했는데,
   어떤 컬럼이 부모 테이블의 PK고, 자식테이블의 FK 일까?
A. 과목번호 + 교수번호
Q. 점수테이블에서 PK 제약조건 설정한다고 하면, 어떤 컬럼으로?
A. 과목번호 + 학번
+) 과목테이블에서 과목번호를 PK로 등록하면,
   그 과목을 가르치는 또 다른 교수를 등록할 수 없음 
 
-- 제 2 정규화 전제조건 : 복합 pk로 구성된 대상만 제2정규화 수행
-- → 식별자를 구성하는 컬럼이 한 개가 아니고, 여러개라는 뜻
--    식별자가 아닌 컬럼들은 식별자 전체 컬럼에 대해 의존적이어야 함
--    식별자가 바뀌는데 나는 안 바뀌어! 하면 의존적이지 않고 독립적인 거임
    << 제 2 정규화 >>
-- 복합 PRIMARY KEY 일 때 수행한다.    
--> 식별자가 아닌 컬럼은 식별자 전체에 대해 의존적이어야 하는데
    둘 다 한테 의존적인 게 아니라, 일부 식별자한테만 의존적이라면
    이를 분리하여 새로운 테이블을 만들어준다.
 
*/



-- 제 3 정규화
--> 식별자가 아닌 컬럼이 식별자가 아닌 컬럼에 의존적인 상황이라면
--  이를 분리하여 새로운 테이블을 생성해 주어야 한다.
--  이 과정을 제 3 정규형이라고 한다.

--> 책: 릴레이션이 제 2 정규형에 속하고,
--      기본키가 아닌 모든 속성이 기본키에 이행적 함수 종속이 되지 않음
-- +) 이행적 함수 종속
--    : 릴레이션을 구성하는 세 개의 속성 집합 X, Y, Z 에 대해 
--      함수 종속 관계 X→Y, Y→Z 가 존재하면 논리적으로 X→Z 가 성립되는데,
--      이것을 Z가 X에 이행적으로 함수 종속되었다고 한다.
--      ex) 고객 릴레이션
--          고객아이디 | 등급 | 할인율
--          고객아이디 → 등급 → 할인율 결정
/*
-- 테이블명 : 과목 → 부모 테이블
--------------------------------------------------------------------------------
과목번호   과목명     교수번호    교수자명   강의실코드   강의실설명
++++++++             +++++++++ 
    P.K
--------------------------------------------------------------------------------
 J0101    자바기초     21        슈바이처     A301       전산실습관 3층 40석 규모
 J0102    자바중급     22        테슬라       T502       전자공학관 5층 60석 규모
 O1123    오라클중급   22        테슬라       A201       전산실습관 2층 30석 규모
 O1150    오라클심화   10        장영실       T502       전자공학관 5층 60석 규모
 J3342    JSP응용      20        맥스웰       K101       인문과학관 1층 90석 규모
                                    :
--------------------------------------------------------------------------------
-- 강의실코드, 강의실설명 : 둘 다 식별자 아닌 컬럼
-- 강의실설명은 강의실코드에 따라서 바뀜
-- 강의실설명은 과목번호, 교수번호가 어떻게 되든 아무 상관 없음
--              강의실코드가 바뀌면 바뀌는 거임
-- → 강의실설명은 강의실코드에 의존적
-- 그러면 | 강의실코드 강의실설명 | 따로 테이블 분리하자 
-- → 이게 제 3 정규화
*/



-- +) 책에서 보고 추가
-- 보이스/코드 정규형(BCNF; Boyce/Codd Normal Form)
-- 릴레이션의 함수 종속 관계에서 모든 결정자가 후보키이면 BCNF 정규형에 속한다.
-- → 후보키를 여러 개 가지고 있는 릴레이션에 발생할 수 있는 
--    이상 현상을 해결하기 위해 제 3 정규형보다 좀 더 엄격한 제약조건을 제시
-- → 보이스/코드 정규형에 속하는 모든 릴레이션은 제 3 정규형에 속하지만,
--    제 3 정규형에 속하는 모든 릴레이션이 보이스/코드 정규형에 속하는 것은 아님

--    ex) 강좌신청 릴레이션
--     고객아이디 | 인터넷강좌 | 담당강사번호
--     ----------   ----------
--           primary key

-- [강좌신청 릴레이션의 후보키]
-- {고객아이디, 인터넷강좌} : primary key
-- {고객아이디, 담당강사번호}

-- [강좌신청 릴레이션의 함수 종속]
-- 고객아이디, 인터넷강좌 → 담당강사번호
-- 담당강사번호 → 인터넷강좌

-- → 담당강사번호가 후보키가 아님에도 인터넷강좌 속성을 결정하고 있음
-- → 고객아이디 | 담당강사번호             담당강사번호 | 인터넷강좌
--    로 테이블 분해
-- → 후보키가 아닌 결정자를 제거하기 위해 분해


--※ 관계(Relation)의 종류

-- 1 : many 관계      (1 : 다 관계)
--> 제 1 정규화를 적용하여 수행을 마친 결과물에서 나타나는 바람직한 관계
--  관계형 데이터베이스를 활용하는 과정에서 추구해야 하는 관계

--> 제 1 정규화 마치면 무조건 부모-자식 테이블 관계가 생성되는데,
--  부모-자식 테이블 구별 방법이 『1 : 다』 관계구조 살펴보면 됨


-- 1 : 1 관계         (1 : 1  관계)
--> 논리적, 물리적으로 존재할 수 있는 관계이긴 하지만
--  관계형 데이터베이스 설계 과정에서 가급적이면 피해야 할 관계

-- ex) 대표적인 1 : 1 관계

-- 학생                       -- 점수
------------------------         ----------------------------------------------
-- 학번 이름   전화 주소          학번  국어점수  영어점수  수학점수  과학점수
------------------------         ----------------------------------------------
-- 7369 홍길동 ...  ...           7369  ....     ....      ....      ....         
  

-- many : many 관계   (다 : 다 관계)
--> 논리적인 모델링에서는 존재할 수 있지만
--  실제 물리적인 모델링에서는 존재할 수 없는 관계

-- 머릿속으로 생각할 때는 될 것 같은데, 실제 설계과정에서는 안되기 때문에
-- 처음 DB 만지는 사람한테는 이 부분에서 실수 엄청나게 많이 나옴 !!
-- FINAL PROJECT 시에, 주의해야 하는 부분 !!

/*
-- 테이블명 : 고객                     - 테이블명 : 제품
-------------------------------------  -----------------------------------------
고객번호  고객명   이메일    전화번호   제품번호 제품명   제품단가 제품설명 ...
-------------------------------------  -----------------------------------------
 1001     최선하   csh@t..   010....   pkdlek   새우깡     600   새우가 들어... 
 1002     김상기   ksk@t..   010....   wkfkfc   자갈치     500   자갈이 들어...
 1003     박현지   phj@t..   010....   rkawkr   감자깡     400   감자가 들어...
 1004     한충희   hch@t..   010....   rhrnak   고구마깡   400   고구마가 들어..
            :                                        :
-------------------------------------  -----------------------------------------
  추가 테이블 만들어줌 → - 테이블명 : 주문접수(판매)
                          ------------------------------------------
                          고객번호  제품번호  주문일자    주문수량
                          ------------------------------------------
                           1001      rhrnak   2022-03-..     10
                           1001      pldlek   2022-03-..     20
                           1002      rhrnak   2022-03-..     20
                                            :
                          ------------------------------------------
-- 논리적으로는
-- 선하는 새우깡, 자갈치, 감자깡, 고구마깡 다 살 수 있고,
-- 새우깡은 선하, 상기, 현지, 충희한테 다 팔릴 수 있을 것 같지만
-- 실제 물리적 모델링에서는 존재하지 않는다.
-- 그래서 실수 많이 일어나는 부분!
-- 다 : 다 관계 테이블은 → 1 : 다 관계 테이블로 쪼개주는 게 필요하다.
--                       → 1 : 다 테이블들로 쪼갬
--> 위에서는 『주문접수(판매) 테이블』생성 
    → 이 과정이 제 4 정규화
*/


-- 제 4 정규화
--> 위에서 확인한 내용과 같이 『many(다) : many(다)』 관계를
--  『1(일) : many(다)』 관계로 깨뜨리는 과정이 바로 제 4 정규화 수행 과정이다.
--  → 파생 테이블 생성 → 다:다 관계를 1:다 관계로 깨뜨리는 역할 수행


-- 여기까지는 순방향의 정규화이고,
-- 이제는 역정규화(비정규화)에 대해 알아보자.


-- 역정규화(비정규화)

-- A 경우 → 역정규화를 수행하지 않는 것이 바람직한 경우 ~!!!
/*
-- 테이블명:부서          -- 테이블명:사원
  10       10      10           10      10      10    10     10      10 Byte
------------------------    -------------------------------------------------
부서번호  부서명  주소       사원번호  사원명  직급  급여  입사일  부서번호
------------------------    -------------------------------------------------
        10개 행                              1,000,000개 행
------------------------    -------------------------------------------------
--> 조회 결과물 (비즈니스 로직 상 이런 결과물이 많이 조회된다)
----------------------------
부서명  사원명  직급  급여
----------------------------
--> 『부서』테이블과 『사원』테이블을 JOIN 했을 때의 크기
--  (10*30Byte) + (1000000*60Byte)
-- 사원 테이블 역정규화
-- 부서명 하나 때문에 부서테이블과 사원테이블 계속 JOIN 해야 하니까,
-- 그냥 부서명 가져와서 사원 테이블에 붙이는 것
       10      10      10    10     10      10 Byte     10 Byte
    ------------------------------------------------- + --------
     사원번호  사원명  직급  급여  입사일  부서번호      부서명
    ------------------------------------------------- + --------
                    1,000,000개 행
    ------------------------------------------------- + --------
--> 『사원』테이블을 역정규화 한 후 이 테이블만 읽어올 때의 크기
--  1000000*70Byte
-- 따로따로 분리되어 있는게 메모리 덜 쓰게 됨 
==> 그러므로, A 경우에는 역정규화를 수행하지 않는 것이 바람직한 경우이다.
*/




-- B 경우 → 역정규화를 수행하는 것이 바람직한 경우~!!!
/*
-- A 경우와 테이블 구조는 같으나,
-- 부서 테이블의 행 개수만 늘어난 것이다.
-- 테이블명:부서          -- 테이블명:사원
  10       10      10           10      10      10    10     10      10 Byte
------------------------    -------------------------------------------------
부서번호  부서명  주소       사원번호  사원명  직급  급여  입사일  부서번호
------------------------    -------------------------------------------------
     500,000개 행                            1,000,000개 행
------------------------    -------------------------------------------------
--> 조회 결과물 (비즈니스 로직 상 이런 결과물이 많이 조회된다)
----------------------------
부서명  사원명  직급  급여
----------------------------
--> 『부서』테이블과 『사원』테이블을 JOIN 했을 때의 크기
--  (500000*30Byte) + (1000000*60Byte) = 15000000 + 60000000 = 75000000
-- 사원 테이블 역정규화
-- 부서명 하나 때문에 부서테이블과 사원테이블 계속 JOIN 해야 하니까,
-- 그냥 부서명 가져와서 사원 테이블에 붙이는 것
       10      10      10    10     10      10 Byte     10 Byte
    ------------------------------------------------- + --------
     사원번호  사원명  직급  급여  입사일  부서번호      부서명
    ------------------------------------------------- + --------
                    1,000,000개 행
    ------------------------------------------------- + --------
--> 『사원』테이블을 역정규화 한 후 이 테이블만 읽어올 때의 크기
--  1000000*70Byte = 70000000
-- 이 경우에는 역정규화를 했을 때, 메모리를 더 절약할 수 있다.
==> 그러므로, B 경우에는 역정규화를 수행하는 것이 바람직한 경우이다.
*/

-- 테이블 구조는 같은데, 안에 데이터 개수만 다른데 
-- 역정규화가 바람직한 경우와 바람직하지 않은 경우가 나뉘었다.

-- → 지금 당장 그 만큼의 데이터를 가지고 있지 않더라도,
--    향후에 그 만큼 데이터를 가질 것 같으면 역정규화 수행할 수 있다.   
--    업무에 대한 자세한 분석이 없으면 역정규화 수행여부 판단하기 힘들다.


-- 오늘 수업 나간 정규화, 역정규화만 잘 파악하고 있어도,
-- final project 시에, DB 설계 과정에 큰 문제 생기지 않을 것이다!

0개의 댓글