#title 컬럼과 로우의 이해 [[TableOfContents]] ==== 사례1 ==== A라는 기업이 있다... 이 기업은 본사를 포함하여 100개의 매장이 있다. 그 회사에서 제조, 판매하는 제품의 종류는 1000개이다. 테이블을 다음과 같은 방식으로 디자인했다. * 매장코드는 001, 002, 003 ... 100 (100개의 컬럼) * 재고테이블은 재고_001, 재고_002, 재고_003 ... 재고_100 이렇게 재고테이블을 매장별로 가져가려고 하는 이유는 매장별로 일일제품재고 리스트를 가져가야 하는데 한 매장당 하루에 1000 종류의 제품이 재고가 있다. 따라서 매장이 100 개이면 한 테이블에 저장했을 경우에 1000(제품종류) * 100(매장수) = 100,000 건이 저장된다. 이처럼 각 제품의 매장별/일별 재고를 관리하려다 보니 하루에 십만건 정도의 데이타가 쌓인다. 그럼 1년이 360일이라면 360 * 100,000 = 36,000,000 (3천 6백만) 건이나 쌓이는 것이 문제가 된다. ==== 무엇이 문제인가? ==== 실제로 쌓이는 데이터는 “각 매장당 상품에 대한 일일 재고량” 이다. 그러므로 위와 같이 테이블을 만들었을 경우는 한 개가 로우에 각 컬럼당 매장이 되는 것이다. 비정규화도 문제가 되지만 현실적으로도 대전에 있는 매장과 부산에 있는 매장이 하나의 개체로 표현된 것이나 마찬가지가 된다. 즉, 개체집합의 의미가 너무 커져 버린다는데 가장 큰 문제가 있다. 하나의 로우는 "매장 전체"를 의미하는 것이 되어 버린다. 이렇게 되면 업무 단위가 매장 전체가 되어 버린다. 제대로 현실을 반영하지 못하는 것은 원천적인 문제이므로 현실과 컴퓨터 세계의 일치성을 가지게 하는 것이 중요하다. 필자는 아래와 같이 테이블을 만들어 보았다. 필요한 정보는 각 매장당 상품에 대한 일일 재고량이다. 그러므로 최근의 데이터를 중점적으로 가져오면 되는 것이다. attachment:design_tuning.jpg 날짜에 DESC로 인덱스를 설정하고, ‘WHERE 날짜 = 현재날짜 AND ROWNUM >= 100’ 또는 하고 ‘SELECT TOP 100 ... WHERE 날짜 = 현재날짜’ 를 한다면 매우 빠르게 현재 날짜를 가져올 수 있다. 사용 패턴에 따라서 결합인덱스를 구성해야 할 수도 있다. 위 그림은 물리적(논리적) ERD이므로 외부키가 보인다. 이처럼 정규화가 안된다면는 복잡한 어플리케이션의 제작이 필요하다. 비정규화로 걸러진 것들에 대해서는 Ordering이 필수적이지는 않지만 개발 시 매우 편리한 속성이 되므로 이러한 약 엔티티 집합에는 일련번호를 부여하는 것도 좋다. 정규화는 중복을 없애고, 데이터의 중복에 따른 여러 가지 문제들을 해결한다. 데이터의 중복을 없애는 것은 데이터의 중복이 있을 때 보다 비용이 절감된다는 의미이며, 이는 곧 최적화에 가까워짐을 뜻한다. 1차 정규화가 하나의 컬럼에 여러 값이 들어갈 수 없다는 것을 해결하기 위해서 여러 개의 값을 펼치는 경우가 대부분의 잘못이다. 그러므로 ‘전화번호1’ , ‘전화번호2’ .... 와 같이 설계해서는 안 된다. 인덱스도 하나만 잡으면 될 것을 여러 컬럼에 잡아야 하는 경우도 생긴다. 그 만큼의 오버헤드를 열심히 코딩하고 있는 부하직원에게 돌리지 말라. 설계한 팀장 잘못이므로... ==== 단점만 있는가? ==== 물론 단점만 존재하지는 않는다. 만약 이 테이블을 사용하는 어플리케이션이 1개 이고, 앞으로 이 테이블을 사용할 다른 어플리케이션이 존재하지 않으며, 매장수가 100개 이상 늘어나지 않는다면 문제의 테이블은 매우 이상적이다. 하지만 이런 경우라면 데이터베이스 시스템이 아닌 파일 시스템이 더 비용이 적게 들어갈 것이다. ==== 사례2 ==== 다음과 같은 테이블이 있다. (사원번호는 기본키) * 사원(사원번호, 이름, 주소, 부양가족1, 부양가족2, 부양가족3) 스키마는 위처럼 되어 있다. 한 명의 사원이 여러 부양가족을 거느린다는 것이다. 만약 부양가족이 3명에서 4명으로 늘었다면 데이터는 어떻게 들어가야 될까? attachment:design_tuning01.jpg 위에서 사원번호 1111인 이재학 사원은 주소가 대구이며, 부양가족은 가족1, 가족2, 가족3이다. 만약 가족4가 생긴다면 위와 같이 해야 할까? 과연 물리적으로 저장될 수 있을까? 당연히 아니다. 왜냐하면 기본키를 사원번호라고 했기 때문에 개체 무결성에 위배된다. 물론 업무규칙이 부양가족은 3명까지만 입력된다고 하면 위와 같은 테이블은 성립될 수도 있다. 어플리케이션에서의 복잡성이 늘어난다. 그러므로 변화와 복잡성을 줄이기 위해서는 다음과 같이 모델링 되어야 한다. 여기서 부양가족은 “약 엔티티 집합” 이 된다. 물론 관계상으로 존재의 유무가 결정되어 지기도 하지만 해당 조직이 성립하기 위해서 꼭 필요한 데이터들의 집합이라면 약 엔티티 집합은 아니다. 아래 그림은 Sybase PowerDesigner에서의 표현법인데 {{{[부양가족]}}}이 {{{[사원]}}}에 종속적임을 나타내는 것이다. 관계가 맺어져 있는데 외부키가 안 보인다고 투덜대지 말기를 바란다. 개념모델에는 외부키라는 개념이 없기 때문이다. attachment:design_tuning02.jpg 비슷한 실제 사례를 보도록 하자. A형, B형 테이블이 각각 있다. 많은 사람들이 어떤 것이 더 효율적인지 묻는 경우를 많이 볼 수 있었을 것이다. attachment:design_tuning02.jpg 컬럼1, 컬럼2, 컬럼3, 컬럼4 ...와 같은 형태로....테이블이 구성된 것을 볼 수 있다. 이것을 왜 B처럼 해 놓았냐고 물어보면 대답은 하나 같다. {{{ [고객] : "A처럼 하면 5건 읽어와야 하는데 B처럼 하면 1건만 읽어오면 되자나요.." [필자] : "ㅡ,.ㅡ;;" [필자] : "데이터베이스 이론 배우셨죠?" … }}} 학교에서 데이터베이스 이론을 공부했거나 배웠는가를 물어보면 다음과 같은 말들이 나온다. {{{ "해봐야 실무에서 필요 없다" "대학 때 배웠다" "배우지 않고 직접 실무에서 익혔다" "안 했다" }}} 보통 이런 말들이다. 제일 열 받는 말은 "해봐야 실무에서 필요 없다" 라는 말이다. 테이블을 저렇게 만들어 놓고 말이다. 다른 분야는 내가 잘 몰라서 뭐라 말은 못하겠지만 데이터베이스는 이론이 없이는 절대 불가능하다. 지금 B형은 안 좋은 전형적인 1차 정규화 위배 테이블이라는 것을 모른다. 이거 일부러 이렇게 했냐고 하면 일부러 그렇게 했다고 한다. 왜냐고 물으면 건수가 줄어들기 때문이라고 한다. 어떤 개발자는 조인을 안해도 되지 않아서 그렇게 했다고 한다. 귀찮거나 조인에 대한 두려움 때문이다. 어이없다. 이런 사람들이 설계를 하니 부실할 수 밖에 없다. 만약 업무가 변화하여 지금은 5개 컬럼으로 감당이 되지만 6 이상으로 늘어난다면 어떻게 할 것인가? 또 컬럼을 늘려줄 것인가? 그럼 어플리케이션도 고쳐져야 할 것이다. 만약 이것들이 필수값이라면 컬럼을 늘려준다고 해도 이전에 있건 값들은 어떻게 할 것인가? 미칠 노릇이다. 데이터베이스의 장점은 변화에 강하다는 것이다. 사람들은 변화에 대한 이해를 잘못하고 있다. 데이터베이스에서의 변화는 테이블의 변화가 아닌 값의 변화인 것을 모른다. ==== 사례2 결론 ==== 123 456 789 는 9바이트다.. 123456789 도 9바이트가 이렇게 성능에 대부분의 비용을 차지하는 I/O에서 똑같은데 구지 변화에 약하고, 여러 가지 이상현상들을 떠안고 가는 것이 올바른지는 모르겠다. 혹자는 위에서 보면 A형은 홍길동을 5번 읽고, B형은 1번만 읽어도 되지 않냐고 할 수 있다. 다른 테이블도 봐야 알겠지만 데이터 중복인 것은 확실하다. 그러나 유추해 볼 때 이것은 필요한 중복(외부키)일 것이다. 거의 미치지 않는 성능과 보다 큰 영향을 미치는 무결성과 어떤 것을 선택하냐는 설계자 마음이다. 중요한 것은 테이블의 구조가 변하면 그에 종속된 어플리케이션들의 수정이 일어날 것은 뻔한 것이고, 개발자들은 고달플 것이다. 데이터가 많이 쌓였을 때 물리적으로 따져보면 오히려 I/O양은 A형 테이블이 더 적을 수 있다. 이유는 B형의 경우 하나의 행이 커서 DBMS의 최소 입출력 단위에 못 채우면 행의 일부가 다른 최소 입/출력 단위(블럭, 페이지..)로 걸치는 체인현상이나 행 마이그레이션이 일어 날 수 있는 소지가 크다. 즉, 클러스터링 팩터가 안 좋아 진다는 것이다. 100개의 행이 하나의 블럭에 있고, 2개의 행이 각각 하나씩의 블럭에 분산되어 있다면 100개의 행을 읽는게 더 빠를 수 있다는 소리이다. 테이블 설계시 최대한 좁게 가는 것이 좋다. 1차 정규화라도 하도록 하자. 복잡한 것은 잘 하는 것이 아니다. 최대한 단순하게 하는 것이 잘하는 것이다.