View Source

* "튜닝"은 SQL 쿼리 또는 데이터 모델을 가리킬 수 있다.
* 이 장에서 언급하고 싶은 것은 데이터 모델을 서투르게 디자인하면 그 어떤 SQL 튜닝도 도움이 되지 않는 것이다.

\\

h2. 6.1 일반적인 데이터 모델을 사용하지 말라
* 애플리케이션이 "최상의 융통성"을 위해 일반적인 데이터 모델 위에 구축되거나 성능에 저해되는 방식으로 구축되는 것은 드문 일이 아니다.
* 예를 들어, 네 개의 테이블을 이용하여 거의 모든 객체를 데이터베이스에 표현할 수 있다

{code:SQL}
create table objects (
oid int primary key
,name varchar2(255)
);
create table attributes (
attrId int primary key
,attrName varchar2(255)
,datatype varchar2(25)
);
create table object_Attributes (
oid int
,attrId int
,value varchar2(4000)
,primary key (oid, attrId)
);
create table Links (
oid1 int
,oid2 int
,primary key (oid1, oid2)
);

insert into attributes values ( 1, 'DATE_OF_BIRTH', 'DATE' );
insert into attributes values ( 2, 'FIRST_NAME', 'STRING' );
insert into attributes values ( 3, 'LAST_NAME', 'STRING' );
commit;

insert into objects values ( 1, 'PERSON' );
insert into object_Attributes values( 1, 1, '15-mar-1965' );
insert into object_Attributes values( 1, 2, 'Thomas' );
insert into object_Attributes values( 1, 3, 'Kyte' );
commit;
insert into objects values ( 2, 'PERSON' );
insert into object_Attributes values( 2, 1, '21-oct-1968' );
insert into object_Attributes values( 2, 2, 'John' );
insert into object_Attributes values( 2, 3, 'Smith' );
commit;

-- 모든 PERSON 레코드의 FIRST_NAME과 LAST_NAME을 가져오는 질의
select max( decode( attrName, 'FIRST_NAME', value, null ) ) first_name
,max( decode( attrName, 'LAST_NAME', value, null ) ) last_name
from objects
,object_attributes
,attributes
where attributes.attrName in ( 'FIRST_NAME', 'LAST_NAME' )
and object_attributes.attrId = attributes.attrId
and object_attributes.oid = objects.oid
and objects.name = 'PERSON'
group by objects.oid;
{code}

\\

!Ex9_1.jpg!

\\

!Ex9_2.jpg!

\\

!Ex9_3.jpg!

\\

* 더 이상 테이블을 생성할 필요가 없다. 필요한 경우(ATTRIBUTES 테이블에 삽입함으로써) 즉시 열을 추가할 수 있기 때문이다.
* 개발자는 제 마음대로 할 수 있으며 DBA는 이들을 제지할 수도 없다. 유연성이 극대화된 셈이다.
* 그렇다면 이 모델의 성능은 어떠하겠는가? 비참, 처참, 끔찍 그 자체이다.
* 이 모델에서는 쿼리 작성이 쉬워 보일 수 있다.
* 예를 들면, 3월에 태어났거나 성이 스미스인 모든 사람들을 조회하고 싶다면 모든 PERSON 레코드의 FIRST_NAME과 LAST_NAME을 구하는 쿼리를 작성하고 그 주위를 인라인 뷰로 감싸면 된다.

{code:SQL}
select *
from ( select max( decode( attrName, 'FIRST_NAME', value, null ) ) first_name
,max( decode( attrName, 'LAST_NAME', value, null ) ) last_name
,max( decode( attrName, 'DATE_OF_BIRTH', value, null ) ) date_of_birth
from objects
,object_attributes
,attributes
where attributes.attrName in ( 'FIRST_NAME', 'LAST_NAME', 'DATE_OF_BIRTH' )
and object_attributes.attrId = attributes.attrId
and object_attributes.oid = objects.oid
and objects.name = 'PERSON'
group by objects.oid
)
where last_name = 'Smith'
or date_of_birth like '%-mar-%';
{code}

\\

!Ex9_4.jpg!

\\

* 쿼리가 쉬워 보인다면 성능에 관하여 생각해 보라! 몇 천 개의 OBJECT 레코드와 수만 개의 OBJECT_ATTRIBUTES가 존재한다고 가정해 보면 오라클은 첫 번째 쿼리를 통해 전체 내부 그룹을 처리하고 WHERE 절을 적용할 것이다.

\\

h2. 6.2 효율성을 염두에 두고 데이터 모델을 디자인하라
* 데이터 모델은 가장 빈번하게 발생하는 쿼리에 대해 가장 효율적으로 응답할 수 있도록 디자인되어야 한다.
* 처음부터 원활한 수행을 목표로 시스템은 디자인하므로 디자인 및 구현 과정에서 정확하게 예측할 수 없을 경우에만 튜닝을 실시한다.

\\

h4. 6.2.1 쓰기 또는 읽기 위주로 디자인할까?
* 팜 모델을 이용한 웹 기반의 내부용 달력 시스템 구축 사례

\\

h4. 6.2.2 어느 모델이 효과적이었을까?
* 사용자의 요구를 데이터 모델에 맞추는 것이 아니라 데이터 모델을 사용자의 요구에 맞추자