일자 데이터 타입이란 YYYYMMDD 형(시/분/초 제외)을 이야기 하는 것이다. 이때 DATE 타입을 선택할 것인가 아니면 VRACHR2(8)을 선택할 것인가의 문제이다. 이것은 성능 문제이기도 하지만 물리 모델링, 개발효율성, 데이터 품질 등을 같이 생각 해야 한다. 물리모델링 시에 많은 모델러들이 일자 데이터 타입과 관련하여 이구동성으로 이야기 하는 것이 아래의 SQL 이다.

 

SELECT ...                     

  FROM ...                     

 WHERE 기준일자 = TO_DATE('20091021', 'YYYYMMDD') ;

 

위의 SQL 에서 일자 컬럼에 시//초가 포함되어 있다면 조회가 되지 않는다.

그렇다고 SQL 을 아래처럼 작성하는 것은 개발효율성이 떨어지고 성능에도 이롭지 못하다.

 

SELECT ...                     

  FROM ...                     

 WHERE 기준일자 BETWEEN TO_DATE('20091021','YYYYMMDD') AND TO_DATE('200910212359', 'YYYYMMDDHH24MISS') ;

 

아니 땐 굴뚝에 연기가 날까?

VARCHAR2(8)을 선호하는 사람들이 주로 이 문제를 제기한다. DATE 타입은 시//초가 들어감으로써 세가지 문제(데이터가 조회되지 않을 수 있고, 개발효율성과 성능이 떨어짐)가 발생함으로  VARCHAR2(8)을 사용해야 한다는 것이다.

 

하지만 과연 이 말이 사실일까? 모든 문제는 모델러가 시//초가 들어갈 수 있게 설계를 했기 때문이다. 왜 그런지 아래 스크립트를 보고 증명해보자

 

-- DATE 형과 VARCHAR2 형을 동시에 가진 테이블 생성                           

CREATE TABLE DT                                                              

( V_DT  VARCHAR2(8 BYTE),                                                    

  D_DT  DATE ) ;                                                                                                                                    

                                                                              

--일자 타입이 date 인 경우에 시//초가 입력될 경우 걸러내는 Constraint.        

--ex) //초를 포함하는 SYSDATE를 입력하면 에러를 발생시키기 위함.                        

--CHECK 절에 OR 가 있는 이유는 NULL 을 허용하기 위해서 이다.

ALTER TABLE DT                                                               

ADD CONSTRAINT D_DT_CHK                                                      

CHECK (D_DT = TRUNC(D_DT) OR D_DT IS NULL) ;        

            

이제 DATE 타입에 정상적인 데이터와 걸러져야 하는 데이터를 INSERT 해보자                            

 

--NULL을 대입해도 에러 발생하지 않음                                                                              

INSERT INTO DT (V_DT, D_DT) VALUES(NULL, NULL) ;                                                  

 

--SYSDATE는 시//초가 들어감으로 INSERT 되면 안됨                                                                              

INSERT INTO DT (V_DT, D_DT) VALUES (NULL, SYSDATE) ;                          

ORA-02290: 체크 제약조건(D_DT_CHK)이 위배되었습니다                   

                   

--에러 발생하지 않음                                                                             

INSERT INTO DT (V_DT, D_DT) VALUES (NULL, TRUNC(SYSDATE)) ;            

 

Constraint는 데이터 품질을 보장해준다

위에서 보는 바와 같이 Constraint는 시//초가 들어가지 않도록 보장해준다.

이것은 성능이 느린 VARCHAR2 타입을 사용하지 말아야 하는 이유가 될 수 있다.

 

어쩔 수 없이 VARCHAR2(8)을 사용하더라도 Constraint를 사용하라

일자 데이터 타입에 VARCHAR2(8)을 사용할 때 날짜가 아닌 데이터가 들어가는 문제도 마찬가지로 해결할 수 있다. 아래처럼 Constraint를 사용하면 된다.

 

--일자 타입이 VARCHAR2 인 경우에 잘못된 데이터를 걸러내는 Constraint .        

--ex) ‘20090230’을 걸러낸다.

--CHECK 절에 OR 가 있는 이유는 NULL 을 허용하기 위해서 이다.                                                  

ALTER TABLE DT                                                               

ADD CONSTRAINT V_DT_CHK                                                      

CHECK (V_DT = TO_CHAR(TO_DATE(V_DT,'YYYYMMDD'), 'YYYYMMDD') OR V_DT IS NULL) ;

 

이제 VARCHAR2(8)에 일자가 아닌 데이터를 넣어보자

 

--230일은 일자가 아니므로 INSERT 되면 안됨                                                                             

INSERT INTO DT (V_DT, D_DT) VALUES ('20090230', NULL) ;                       

ORA-01839: 지정된 월에 대한 날짜가 부적합합니다

 

--에러 발생하지 않음                                                                             

INSERT INTO DT (V_DT, D_DT) VALUES ('20090228', NULL) ;  

 

성공적으로 일자가 아닌 String 을 걸러 내었다.

 

결론

이제 알 것이다.  Constraint가 데이터 품질을 향상시키고 개발효율성을 높이며 성능에 까지 영향을 끼친다는 사실을

반박하라. //초가 들어감으로 VARCHAR2(8)을 사용해야 한다는 주장을

사용하라. 성능과 데이터 품질을 향상 시키는 DATE 타입을

오라클에서 시/분/초는 제외하고 일자만 저장되는 데이터 타입을 제공한다면 하는 아쉬움이 진하게 남는다.  미래의 버젼에 이러한 요구사항을 해결한 데이터 타입이 나오길 기대한다.


PS

DATE 타입을 성급히 적용하면 안 된다. 기존의 시스템은 일자가 아닌 데이터도 문제 없이 처리가 되었을 것이다. 하지만 DATE 타입으로 바꾸면 에러로 떨어진다. 따라서 에러의 처리정책과 처리Logic 등이 세워진 이후에 적용하라. 이런 골치 아픈 문제 때문에 VARCHAR2(8)을 사용하는 것은 말이 안 된다. 원칙적으로 에러로 떨어지는 것이 정당한 것이고 데이터를 수정하고 다시 처리하는 것이 옳다.

  

아래는 DATE 타입과 VARCHAR2 타입의 장단점 이므로 참고하기 바란다.

----------------------------------------------------------------------------------------------------------------------



성능상으로는 DATE 형이 우월하다

여기에는 두 가지 이유가 있다.

 

첫 번째, DATE 타입은 옵티마이져가 날짜임을 인식한다.

 

SELECT ...                     

  FROM ...

 WHERE 기준일자 BETWEEN to_date('20091020', 'yyyymmdd') and to_date('20091021', 'yyyymmdd)

 

기준일자는 DATE 타입이다. 위의 조건대로라면 옵티마이져는 정확히 이틀간(10 20일부터 21일까지)이라는 것을 인지한다. 따라서 옵티마이져는 인덱스로 SCAN 할 것인지 아니면 FULL TABLE SCAN 할 것인지 판단할 수 있다.

 

하지만 기준일자가 아래처럼 VARCHAR2(8) 타입이라면 달라진다.

 

SELECT ...                      

  FROM ...

 WHERE 기준일자 BETWEEN '20091020' and '20091021'

 

옵티마이져는 '20091020'가 일자인지 인식 하지 못한다. 따라서 이틀 치의 데이터를 조회한다는 사실을 인식하지 못한다. String 타입이므로 이것은 당연한 것이다.

 

두 번째, DATE 타입은 7 byte를 차지하고 VARCHAR2(8) 8 byte를 차지한다.

 

두 가지 이유에 의해서 성능은 DATE 타입이 조금이라도 우월함을 알 수 있다.  

 

그럼에도 불구하고 DATE 타입을 사용하지 않는 가장 큰 이유는 무엇일까? 크게 3가지 이유로 요약된다.

 

첫 번째, DATE 타입의 문제점은 sysdate 등을 입력할 경우 시//초 가 들어감으로 SQL을 실행하면 결과값이 나오지 않는다.(이 문제는 위에서 이미 언급되었음)

 

두 번째, 년도나 월 데이터를 조회할 때 LIKE를 사용하지 못하고 BETWEEN 을 사용해야 한다는 것이다. 이것은 큰 문제라고 보지 않는다. 조건절이 조금 길어질 뿐 INDEX RANGE SCAN 이라는 점은 같기 때문이다.

 

세 번째, SYSDATE 등을 INSERT할 때 TRUNCT 등의 함수를 사용하여 시//초 등을 잘라내야 한다.

                

그렇다면 VARCHAR2 타입의 문제점은 없는가?
크게 3가지의 문제점이 있다.

 

첫 번째, 성능문제(옵티마이져가 일자인지 알 수 없음)

이 문제는 ORACLE 11g를 사용하면 더욱 심각한 차이가 발생할 수 있다.

왜냐하면 Bind Aware 기능이 강화되었기 때문에 변수를 마치 상수처럼 취급할 것이고 이에 따라 DATE 타입이 성능 면에서 훨씬 우월해 질것이다. 

                 

두 번째, VARCHAR2 타입의 문제점은 날짜가 아닌 데이터가 들어갈 수 있다는 것이다. 예를 들면 'ABCDEFGH' 혹은 '20090231' 등의 잘못된 데이터가 입력될 수 있다. 이것은 데이터 품질에 치명적이다. 혹자는 'DATE 타입도 시//초가 들어가므로 마찬가지 아니냐' 라고 생각할 수 있지만 근본적으로 다르다. DATE 타입을 사용하면 시//초는 TRUNC 등의 함수를 사용하여 Cleansing할 수 있지만 VARCHAR2는 그렇게 할 수 없다.

 

예를 들어 '20091032' 라는 데이터를 Cleansing 해야 한다고 치면 10 31일로 할 것인가? 아니면 11 1일로 할 것인가? 이것은 함부로 판단할 수 없는 것이다. 혹자는 'CHECK 기능을 추가하여 모든 프로그램에서 일자가 아닌 것을 CHECK 하면 되지 않냐?' 라고 할 수 있다.

 

맞는 말이다. 하지만 프로그램을 사용하지 않고 직접 DB KEY IN 하여 INSERT 할 수도 있기 때문에 원천적으로 원인을 제거한다고 볼 수 없다.(급한 경우에는 이렇게 하기도 한다)

 

세 번째, 성능문제에도 불구하고 개발편의성을 위해 VARCHAR2를 사용하였지만 일자연산이 발생하면 오히려 개발생산성이 저하된다.

 

예를 들면 일자끼리 빼서 차이를 본다든지 아니면 일자에 며칠을 더해서 본다든지 일자에 연산이 일어날 경우 오히려 TO_DATE 등의 함수를 사용해야 한다. 이런 경우는 자주 발생되는 편이다.

 

이상으로 DATE 타입과 VARCHAR2 타입의 장단점을 살펴보았다.

이 글과 관련된 POST 도 참고하기 바란다.

http://ukja.tistory.com/265

Posted by extremedb

댓글을 달아 주세요

  1. feelie 2009.10.21 19:14  댓글주소  수정/삭제  댓글쓰기

    궁금한 내용중하나였는데 감사합니다.
    최근에 로그성 테이블을 만들면서 timestamp로 할것인지, varchar2로 할것인지를 생각하다가 조건에 사용되는 날짜는 varchar2로 해야 한다는 말이 생각나서 여러가지를 고려하지 않고 varchar2로 했습니다.
    로그성 테이블을 timestamp로 사용한 이유는 초까지 중복이 발생하는 경우가 많아서입니다.
    내용은 date type에 대한 내용인데요 내용상 timestamp도 동일하게 생각하면 되는겁니까?
    그리고 시분초를 포함해야 하는 경우에는 date, timestamp가 옳은 결정인가요? (문자열 조회조건, 간혹 시간차도 필요)

    • Favicon of https://scidb.tistory.com BlogIcon extremedb 2009.10.21 23:12 신고  댓글주소  수정/삭제

      TIMESTAMP 의 성능은 제가 사용해보지 않았기 때문에 뭐라고 말씀드릴수가 없네요.
      하지만 시분초까지만 필요하시다면 DATE 타입을 사용하시느것이 좋을것입니다.

    • Favicon of http://ukja.tistory.com BlogIcon 욱짜 2009.10.22 13:26 신고  댓글주소  수정/삭제

      TIMESTAMP는 DATE의 다음 버전, 더 정확하게 말하면 ANSI 버전으로 생각하면 될 것 같습니다. 국제표준에 맞는 시간표현 데이터 타입이라고 보면 되지 않을까요? TIMESTAMP와 더불어 INTERVAL이 추가되면서 시간 계산에 있어 거의 무한한 자유가 주어졌죠. TIMESTAMP(0)으로 하면 DATE와 동일하니까 시분초까지만 필요한 경우에도 사용할 수 있구요.

      개인적으로 TIMESTAMP에서 제가 가장 유용하다고 생각하는 것은 시간 연산의 결과가 더 직관적이라는 것입니다. 아래 결과를 보면 TIMESTAMP를 사용한 경우 시간의 차이가 역시 시간으로 표현되기 때문에 훨씬 직관적이죠. 반면에 DATE는 1일 기준의 초로 표현되기 때문에 역으로 연산을 해야하는 단점이 있습니다.

      UKJA@ukja1106> create table t1(c1 date, c2 date, c3 timestamp(0), c4 timestamp(0));

      Table created.

      Elapsed: 00:00:00.03
      UKJA@ukja1106>
      UKJA@ukja1106> insert into t1(c1, c3) values(sysdate, systimestamp);

      1 row created.

      Elapsed: 00:00:00.00
      UKJA@ukja1106>
      UKJA@ukja1106> exec dbms_lock.sleep(10);

      PL/SQL procedure successfully completed.

      Elapsed: 00:00:10.01
      UKJA@ukja1106>
      UKJA@ukja1106> update t1 set c2 = sysdate, c4 = systimestamp;

      1 row updated.

      Elapsed: 00:00:00.00
      UKJA@ukja1106>
      UKJA@ukja1106> select c2 - c1, c4 - c3 from t1;

      C2-C1
      ----------
      C4-C3
      ---------------------------------------------------------------------------
      .000115741
      +000000000 00:00:10


      UKJA@ukja1106> select .000115741*24*60*60 from dual;

      .000115741*24*60*60
      -------------------
      10.0000224

    • Favicon of https://scidb.tistory.com BlogIcon extremedb 2009.10.22 13:34 신고  댓글주소  수정/삭제

      그렇네요. 직관적이고 초로 표현되므로 역으로 환산할 필요도 없구요. 덕분에 몰랐던것을 알게 되었습니다. 고맙습니다.
      DBMS 종류별(sql server, oracle, db2)로 timestamp ,datetime 그리고 date 등을 한번 정리해야 할 필요가 있을거 같습니다. 이부분이 많이 혼동되는 것이 사실입니다.

  2. Favicon of http://ukja.tistory.com BlogIcon 욱짜 2009.10.22 08:47 신고  댓글주소  수정/삭제  댓글쓰기

    깔끔합니다! 이런 접근이 필요했습니다. ^^

  3. 마늘장아찌 2009.10.23 15:14  댓글주소  수정/삭제  댓글쓰기

    좋은정보 감사합니다.
    =================
    실무에서 항상 고민해 봤었던 내용인데 명쾌하게 정리를 해주셨네여.
    과거 informix DB에서는 Date 와 Datetime으로 구분이 가능했고 interval이나 timestamp
    개념이 있었던 기억이 새록새록 납니다. 그게 벌써 10년도 더된 이야기네요...

  4. 만두사랑 2009.10.28 13:57  댓글주소  수정/삭제  댓글쓰기

    좋은글 감사합니다.
    도움이 많이 되네요^^

  5. 김시연 2009.10.28 16:41  댓글주소  수정/삭제  댓글쓰기

    오랜만에 방문해서 그동안 쓰신글을 한번 쭉 보고 갑니다~! 올때마다 새로운것을 알고 가니 참 좋습니다. 요즘은 필드를 거의 안나가서 직접적으로 오라클을 할일이 없긴한데, 필드에 나가게 되면 응용해보도록 하겠습니다.

    그리고 집필활동은 잘 되고 계신가요? 시간을 쪼개서 책을 쓰시는것이 참 어려운일일텐데... 건강에 유의하시고요.

    그럼 다음에 또 들르겠습니다.

    PS) 혹시 JVM에 관심있으시면 http://wiki.ex-em.com/index.php/Java_Performance_Fundamental 여기한번 들러보세요~!

    • Favicon of https://scidb.tistory.com BlogIcon extremedb 2009.10.28 17:45 신고  댓글주소  수정/삭제

      오랜만입니다.
      저번에 프로젝트 5박 6일 이었던가요? 엑셈블로그에서 잘읽었습니다.
      엑셈에서 JVM 책을 발간하셨네요.
      JAVA 는 잘 모르지만 JDBC 쪽은 꼭보도록 하겠습니다.
      그리고 집필작업은 예정되로 진행되고 있습니다.
      성원에 감사드립니다.

  6. 2009.10.29 15:51  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  7. 2010.06.28 17:13  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  8. Ejql 2010.07.26 15:43  댓글주소  수정/삭제  댓글쓰기

    한번씩 쭉 읽고 있습니다. 이번글은.. 가장 작은 전쟁중에 하나인것 같은데.. 정리를 잘 해 주셨습니다.
    감사합니다.

  9. Favicon of https://ccmpraise.tistory.com BlogIcon 주한길 2013.01.23 00:53 신고  댓글주소  수정/삭제  댓글쓰기

    좋은글 출처를 표시하고 블로그에 담아갑니다. ^^