Database/Oracle2009. 2. 18. 23:30

날짜형 데이터에 대해서

 
이번연제는 데이타베이스에 반드시 따라오는(?) 날짜 데이타를 뽑아 취급하는 SQL에 대해 해설합니다. 날자 데이터는, 스케줄 관리나 일자, 기간마다의 집계, 혹은, 특정의 기간을 지난 데이터를 대상으로 한 메인테넌스 등, 데이터 그 자체로서 또 어떤 종류의 데이타를 뽑아 취급하기 위해서 필요 불가결한 키로서 중요한 데이터입니다.
그러나 보통 수치와 같이 취급하려면 조금 귀찮습니당..그중에서도 역시 날자 데이터의 연산입니다.60진법, 24진법, 혹은 또 매월의 날짜가 다른 등, 외곬줄에서는 가지 않은 것은 여러분도 다들 아시는 바지요.이것을 잘 취급하기 위해, Oracle에는 일자 데이터에 적절한 데이터형이 준비되어 있습니다.또, 이것들 날짜 시각형의 데이터를 취급하기 위한 함수도 준비되어 있습니다.지금부터 Oracle에 종사하는 분은 반드시 기억할 필요가 있습니다.벌써 날짜 데이터의 취급에 익숙하고 계시는 분도 지금 한 번 정리를 위해서 읽어 주삼!!
 
파라메터를 확인해 두자!!

 
우선 처음에, 날짜 데이타를 뽑아 사용할때에 주의해 두지 않으면 안 되는 파라미터가 있기 때문에, 이것에 대해 정리해 둡시다.
날짜 포맷으로 잘 사용하는 NLS 파라미터는 아래와 같습니다.
  • NLS_DATE_FORMAT
  • NLS_TIMESTAMP_FORMAT
  • NLS_TIMESTAMP_TZ_FORMAT
NLS 관련의 파라미터는, 아래와 같은 뷰로 확인할 수 있습니다.한 번 확인해 보세요.
  • NLS_DATABASE_PARAMETERS
  • NLS_INSTANCE_PARAMETERS
  • NLS_SESSION_PARAMETERS
이번 체크해 두어야 할 파라미터는, NLS_SESSION_PARAMETERS로 확인할 수 있습니다.
SQL> select * from nls_session_parameters;

PARAMETER                      VALUE
------------------------------ ------------------------------
……생략……
NLS_DATE_FORMAT                RR-MM-DD
NLS_TIMESTAMP_FORMAT           RR-MM-DD HH24:MI:SSXFF
NLS_TIMESTAMP_TZ_FORMAT        RR-MM-DD HH24:MI:SSXFF TZR
……생략……
리스트1 NLS파라메터의 확인
 
상기와 같이 날짜 데이터의 표시 포맷이 NLS 파라미터에 의해서 정의되고 있습니다.이것들은 각각의 환경에 적절히 설정되므로 같은 SQL를 실행해도 표시가 달라 당황하는 일이 있습니다.
 
날짜데이터의 연산

 
그럼 상기를 근거로 하고, 일자 데이터의 조작에 대해 해설합니다.우선은, 연산 방법을 보고 갑시다.
 
Oracle9i에서는 DATE형에 가세하여 TIMESTAMP형이라고 하는 데이터형이 추가되어 있습니다. 이것도 날짜 데이터형입니다.취급할 수 있는 데이터의 정도가 다릅니다만, 이하의 연산에 대해서는 DATE형의 데이터와 같게 사용할 수 있습니다.아래와 같이에는, SYSDATE 함수(DATE형), SYSTIMESTAMP 함수(TIMESTAMP형)를 사용한 연산을 해보지요....
 
【일자 데이터의 가(감) 산】
일의 가(감) 산
SYSDATE + 1 SYSTIMESTAMP + 1 (*1)
   
시간의 가(감) 산(1/24)
SYSDATE + 1/24 SYSTIMESTAMP + 1/24 (*1)
   
분의 가(감) 산(1/24/60)
SYSDATE + 1/24/60 SYSTIMESTAMP + 1/24/60 (*1)
   
초의 가(감) 산(1/24/60/60)
SYSDATE + 1/24/60/60 SYSTIMESTAMP + 1/24/60/60 (*1)
   
달의 가(감) 산(ADD_MONTHS)
ADD_MONTHS(SYSDATE,1) ADD_MONTHS(SYSTIMESTAMP,1) (*2)
   
연의 가(감) 산(ADD_MONTHS)
ADD_MONTHS(SYSDATE,1*12) ADD_MONTHS(SYSTIMESTAMP,1*12) (*2)

 
ADD_MONTHS 함수의 주의 사항
제1 인수(DATE형)로 지정한 날이 그 달의 마지막 날의 경우는, 결과의 날도 마지막 날이 됩니다.또, 제1 인수로 지정한 날보다 결과의 달의 날이 적은 경우도 마지막 날이 결과가 됩니다.예를 들면, 제1 인수에 6월 30일, 제2 인수에 1을 사용해 ADD_MONTHS 함수를 실행하면, 그 결과는 7월 31일이 됩니다.7월 30일은 아니므로 조심하시길....
 
(*1)TIMESTAMP WITH TIME ZONE형으로부터 DATE형에 변환되므로 주의.DATE형으로 변환하고 싶지 않은 경우는, 후술의 TO_DSINTERVAL 함수 등 기간 데이터형을 사용한다.
 
(*2)TIMESTAMP WITH TIME ZONE형으로부터 DATE형에 변환되므로 주의.DATE형으로 변환하고 싶지 않은 경우는, 후술의 TO_YMINTERVAL 함수 등 기간 데이터형을 사용한다.
 
어떠십니까??? 상기의 계산방법은 비교적 자주 사용합니다. 이것을 사용할 수 있으면, 어플리케이션으로 필요한 대부분의 날짜 처리를 기술하는 것이 가능하지 않을까요.연산의 단위가 1일인 것을 인식할 수 있으면, 기억하는 것도 간단하네요.ADD_MONTHS 함수는 월말일의 취급에 주의가 필요함으로 주의를 기울이시기를....
 
 
특정일 취득

 
그럼 이제부터는 간단할 것 같은데 잘 생각나지 않는 날짜 취득에 대해서 보고 갑시다.
우선은, 이것입니다.
 
【특정의 달의 마지막 날을 취득한다(LAST_DAY)】
  • LAST_DAY(SYSDATE)
  • LAST_DAY(SYSTIMESTAMP)  
이것은 월차 처리 등에 관련해서 사용되는 일이 있습니다만, 사용할 기회가 없는 개발자에게 있어서는 인연이 먼 함수의 하나입니다.사용법은 간단하고, 취득하고 싶은 달의 일자를 인수에 건네주면 OK입니다.
 
윤년도 판단해 주므로, 예를 들면 2월의 마지막 날도 문제 없게 취득할 수 있습니다.

그럼, 월초는 어떻게 취득할까요? 우선 주의 첫날을 취득해 봅시다.
 
【주의 첫날을 취득한다(TRUNC)】
자주 스케줄의 확인을 할 때에, 「00의 주」라고 하는 표현을 합니다. 즉 주의 선두의 날을 가리키고 있습니다만, 이것을 Oracle로 취득하는 경우는 어떻게 하면 좋을까요?
이 경우는 TRUNC 함수를 사용합니다.수치를 짤라나거나 할 때에 사용하는 함수입니다만, 실은 날자 데이터에도 사용할 수 있습니다.
 
그럼, 이번 주의 처음의 날을 취득해 보겠습니다.
SQL> !cal
      2月 2005
日 月 火 水 木 金 土
       1  2  3  4  5
 6  7  8  9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28
리스트 2 캘린더의 표시
 
    오늘은 2月16日입니다.
 
SQL> select SYSDATE from dual;

SYSDATE
--------
05-02-16
리스트 3 오늘의 일자를 확인
 
    TRUNC 함수의 제2 인수에'day'를 이용하면…….
SQL> select TRUNC(SYSDATE,'day') from dual;

TRUNC(SY
--------
05-02-13
리스트 4 이번 주의 주처음의 날을 취득
 
이러한 방법으로 간단하게 주의 처음의 날을 취득할 수 있습니다. 물론 SYSDATE 함수 대신에, SYSTIMESTAMP 함수를 사용할 수도 있습니다.다만, DATE형으로 변환되는 것은 기억해 둘 필요가 있습니다.
날짜 데이터로 TRUNC 함수를 사용하는 경우, 제2 인수에는'day'외에도 몇개인가 지정할 수 있습니다.메뉴얼을 확인하삼!!
 
TRUNC 함수를 사용한 샘플 SQL를  1개들어 둡니다.제목은 「이번 달의 제n○요일을 취득한다」
select decode(TRUNC(get_date,'mm'),TRUNC(SYSDATE,'mm'),get_date,null)
from (
select decode(TRUNC(TRUNC(TRUNC(SYSDATE,'mm'),'day') + :youbi -1,'mm')
             ,TRUNC(SYSDATE,'mm')
             ,TRUNC(TRUNC(SYSDATE,'mm'),'day') + :youbi -1
             ,TRUNC(TRUNC(SYSDATE,'mm'),'day') + :youbi -1 + 7)
      + (:nambanme - 1) * 7 get_date
from dual)
리스트 5 이번 달의 제n○요일을 취득한다
 
youbi 변수에 일요일(1)부터 토요일(7)까지 상당하는 숫자를 설정합니다.또, nambanme 변수에 몇 번(주) 째의 요일을 취득하는지를 설정합니다.굳이 SQL로 기술했습니다만, 여기까지 한다면 보통은 FUNCTION로 하겠지요.
 
【요일의 처리는 NEXT_DAY 함수로】
그러고 보면, Oracle의 함수에 요일을 취급할 수 있는 함수가 있던 것 같은……
NEXT_DAY 함수입니다.NEXT_DAY 함수는“지정한 날”의 다음의“지정한 요일”에 상당하는 날을 취득해 주는 함수입니다.상기의 SQL도 요일을 취급하고 있으므로, 실은 NEXT_DAY 함수를 사용하면 심플하게 됩니다.
 
select decode(TRUNC(get_date,'mm'),TRUNC(SYSDATE,'mm'),get_date,null)
from (
select NEXT_DAY(TRUNC(SYSDATE,'mm')-1,
                :youbi) + 7 *( :nambanme -1 ) get_date
from dual)
리스트 6 이번 달의 제n○요일을 취득한다(NEXT_DAY 버젼)

외측에 대상월을 빗나갔을 경우의 핸들링용으로 DECODE 처리를 쓴것을 제외하고 실제로 일자를 취득하는 부분은 단 1행만으로 되었습니다.덧붙여서, NEXT_DAY 함수의 제2 인수에는, 요일을 그대로 지정합니다.예를 들면, '월요일'나, 'MON'라고 하는 상태입니다.
 
함수를 잘 사용하면, 같은 결과를 돌려주는 SQL를, 보다 심플하게 고쳐 쓸 수 있습니다. 그러므로 어쨌든 여러가지 함수에 접해 둘 필요가 있습니다. 모르면 사용하려고는 생각하지 않으니까.그럼 좀 더 가 봅시다.
 
【일자 시각의 사사오입(ROUND)】
다음과 같은 경우는 어떻게 하면 좋을까요.예를 들면,××시 이후에 입력된 처리는 다음날 취급으로 하고 싶다.
 
이것에는 사사오입을 사용합니다.물론, 그 밖에 방법이 없는 것은 아닙니다만, 이번은 철저하게 일자 데이터에 사용할 수 있는 함수를 사용합니다.
 
예를 들면, 12시 이후는 다음날 취급으로 하는 경우, 11시 59분 59초까지는 2월 14일입니다만…….
 
SQL> select round(to_date('2005/02/14 11:59:59',
                         'yyyy/mm/dd hh24:mi:ss')) from dual;

ROUND(TO
--------
05-02-14
리스트 7 12시 이후는 다음날 취급으로 한다(11시 59분 59초)
  
    12시 정확히에서는 다음날로 바뀝니다.
 
SQL> select round(to_date('2005/02/14 12:00:00',
                         'yyyy/mm/dd hh24:mi:ss')) from dual;

ROUND(TO
--------
05-02-15
리스트 8 12시 이후는 다음날 취급으로 한다(12시 정확히)
 
    12시는 아니고 오후 6시를 변환 타이밍으로 하고 싶은 경우는, 6시간 시프트 시키면 됩니다.
 
SQL> select round(to_date('2005/02/14 12:00:00',
                    'yyyy/mm/dd hh24:mi:ss')-6/24) from dual;

ROUND(TO
--------
05-02-14

SQL> select round(to_date('2005/02/14 18:00:00',
                    'yyyy/mm/dd hh24:mi:ss')-6/24) from dual;

ROUND(TO
--------
05-02-15
리스트 9 오후 6시 이후는 다음날 취급으로 한다
 
실은 (벌써 여러분 깨달아지고 있다고 생각합니다만) 어차피 시각을 시프트 시킬 필요가 있다면, 일부러 ROUND 함수를 사용하지 않고, 최초부터 시프트 시키고 싶은 시간을 가감 헤아리면 같은 결과가 됩니다. 조금 배경 설정에 무리가 있었는지요. 
 
기간의 취득

 
그런데 그런데, 상기에서는 특정의 날을 취득하는 방법을 소개했습니다.일자 데이터에 관한 취급으로서 이제 1개 해두고 싶은 포인트가 있습니다.그것은 기간입니다.

 【달의 차분(MONTHS_BETWEEN)】
특정의 날과 일의 사이의“날짜”를 요구하는 경우는, 매우 간단합니다.DATE형의 뺄셈으로 요구할 수 있군요.이 경우, 예를 들면 아래와 같은 SQL로, 「195일간」이라고 하는 결과를 취득할 수 있습니다.
 
SQL>  select   to_date('2005/02/14','yyyy/mm/dd')
  2          - to_date('2004/08/03','yyyy/mm/dd') duration
  3   from dual;

  DURATION
----------
       195
리스트 10 특정의 날과 일의 사이의“날짜”를 요구한다
 
이것은 간단하네요.그럼, 이것을 「6개월과 11일간」이라고 하는 표현으로 하려면  어떻게 할까요.이쪽의 표현이 기간의 길이를 감각적 알기 쉬울수도 있겠네요
 
이런 경우, MONTHS_BETWEEN 함수가 편리합니다.TRUNC 함수나, ADD_MONTHS 함수로 조합해 표시해 보겠습니다.
 
select  TRUNC(months_between( date2, date1 )) months
      , TRUNC(date2
              - ADD_MONTHS( date1, 
                       TRUNC(months_between( date2 ,date1 )))
            ) days
from dual
리스트 11 함수를 조합한 기간의 계산
 
    date1와 date2에 방금전의 일자를 세트해 실행해 봅시다.다음과 같은 결과를 얻을 수 있습니다.
 
SQL> select  TRUNC(months_between( to_date('2005/02/14','yyyy/mm/dd')
  2                    , to_date('2004/08/03','yyyy/mm/dd') )) months
  3    , TRUNC(to_date('2005/02/14','yyyy/mm/dd')
  4      - ADD_MONTHS( to_date('2004/08/03','yyyy/mm/dd')
  5        , TRUNC(months_between( to_date('2005/02/14','yyyy/mm/dd')
  6                           ,to_date('2004/08/03','yyyy/mm/dd') )))
  7          ) days
  8  from dual;

    MONTHS       DAYS
---------- ----------
         6         11
리스트 12 함수를 조합한 기간의 계산(실행예)
 
MONTHS_BETWEEN 함수는 단순하게 일자와 일자의 사이에 몇개월간 있을까를 세어 주는 함수입니다. 그래서 연산 결과에 끝수가 나와 버려, 그대로 표시시키면 「2.2345개월간」과 같이 이해하기 어려워져 버립니다.이것도 먼저 TRUNC 함수로 짤라내는 등의 요건에 응한 수치에 가공해 표시시키는 것이 좋게쪄.....
이번회에서는 몇개인가 날짜자 데이터의 연산을 가 보았습니다. 거의 사용한 적이 없는 함수도 있었을지도 모르겠네요 다음 회는 날짜에 관한 데이터형, Oracle9i로부터 증가한 TIMESTAMP형, 기간 데이터형에 대해 해설할 예정입니다.(다음 회에 계속)
 
출처 블로그 > KACHISORI
원본 http://blog.naver.com/hirokorea/20028412436
본 자료는 일본 @IT(http://www.atmarkit.co.jp/fdb/index/index-db.html#sqlclinic)에 株式会社インサイトテクノロジー의 大道隆久씨가 연재한 SQLクリニック를 번역 재구성한 것입니다.
 
DATE형과 TIMESTAMP형

날짜를 격납하는 데이타형으로서는 종래의 DATE형에 가세해 ´Oracle9i로부터 TIMESTAMP형을 사용할 수 있게 되었습니다
 
DATE형 최소단위=1초
TIMESTAMP형 최소단위=10억분의 1초(10의 9승 초)
 
 
 
 
「10억분의 1초」단위로 관리할 필요가 있는 것은 어떤 데이터일까요..과학기술 관련이나, 공업 실험등의 데이터? 혹은 자동차 레이스의 데이터? 너무 구체적으로 이미지 할 수 없습니다만, 어쨌든 정확도가 향상되었습니다. 리스폰스 통계를 내는데 편리라고 하는 기술을 어디선가 본적이 있습니다만 실제로는 어떤 데이터가 들어가는지 보고 싶네요...
 
데이터의 형태가 다르므로 그 사이즈 또한 다릅니다.물리 설계 시에는 주의하삼..
 
데이타형 데이터 사이즈
DATE 7bytes
TIMESTAMP([0-9]) 7~11bytes
표 1 TIMESTAMP형과 DATE형의 데이터 사이즈
 
 
 
 
 
 
 
게다가 TIMESTAMP형의 데이터는 타임 존의 정보를 가질 수 있습니다.이 형태로 정의한 열의 사이즈가 바뀌기 때문에 주의해야 합니다.
 
데이터형 데이터 사이즈
TIMESTAMP([0-9]) WITH TIME ZONE 13bytes
TIMESTAMP([0-9]) WITH LOCAL TIME ZONE 7~11bytes
표 2 타임 존  TIMESTAMP형의 데이터 사이즈
 
 
 
 
 
 
날짜 데이터를 취득하는 함수안에는, TIMESTAMP형의 데이터를 취득하기 위한 함수도 있습니다.각각 아래와 같이에 나타낸 데이터형의 값을 돌려줍니다.
 
함수 데이터형
SYSDATE DATE
SYSTIMESTAMP TIMESTAMP WITH TIME ZONE
CURRENT_DATE DATE
CURRENT_TIMESTAMP TIMESTAMP WITH TIME ZONE
LOCALTIMESTAMP TIMESTAMP
표 3 일시 데이터를 취득하는 함수와 데이터형
 
 
 
 
 
 
 
 
 
 
 
즉 DATE형의 함수로 취득한 데이터는 DATE형(주 1)의 열에, TIMESTAMP형의 함수로 취득한 데이터는 TIMESTAMP형(주 2)의 열에 격납한다고 하는 느낌의 사용법이 됩니다.
(주 1) DATE형의 데이터를 TIMESTAMP형의 열에 격납해도 문제 없습니다.이 경우는, 1초 미만의 데이터는 세트 되지 않습니다.반대로 TIMESTAMP형의 데이터를 DATE형의 열에 격납할 수도 있습니다.물론, 1초 미만의 정보는 삭제됩니다.
 
(주 2) 타임 존 정보의 유무에 의해 TIMESTAMP WITH (LOCAL) TIME ZONE형을 사용하는 경우도 있습니다.
상기의 함수에 대해서, 간단하게 해설해 둡니다.
상기의 함수는 아래와 같이 크고 2개로 나눌 수 있습니다.
  • 시스템일시
      SYSDATE
      SYSTIMESTAMP
     
  • 세션일시
      CURRENT_DATE
      CURRENT_TIMESTAMP
      LOCALTIMESTAMP
시스템 일시로 분류한 함수에서는, OS의 일시를 취득할 수 있습니다.즉, 보통은 그 서버가 설치되어 있는 장소의 시각입니다.한편 세션 일시로 분류한 함수에서는, 그 세션의 타임 존에 변환된 일시를 취득할 수 있습니다.
 
 
기간 데이터 형

그런데 이번 연제에서 주목해 두고 싶은 것은 기간 데이터형입니다.
  • 데이터형
     INTERVAL YEAR TO MONTH
     INTERVAL DAY TO SECOND
전회의 일자 데이터의 연산에서는 시간이나 분의 연산 시에,1/24(1시간) 혹은1/24/60(1분간)을 가감 해서 필요한 일시를 취득했습니다.
 
처음으로 일자 데이터의 연산을 접한 프로그래머는 나눗셈의 연속으로 도출하는 방법으로 「머여 이게」라고 생각되겠지만 그렇지만 익숙해진다고 심플한 연산으므로 곧바로 응용할 수 있습니다. 예를 들면, 1초간은1/24/60/60일이군요.그리고는, 필요한 시간분 각각 곱셈 하면 OK입니다.
 
현재 시각부터 3시간 25분 30초전의 시각을 도출할 때는
SQL> SELECT
       SYSDATE - ( 3*1/24 + 25*1/24/60 + 30*1/24/60/60 )
     FROM dual;
리스트 1 현재 시각부터 3시간 25분 30초전의 시각을 도출( 1)
 
    이 됩니다 또는 한방에
 
SQL> SELECT SYSDATE - 12330/86400 FROM dual;
리스트 2 현재 시각부터 3시간 25분 30초전의 시각을 도출( 2)
   
(이)라고 해도 같은 결과를 얻을 수 있습니다.
 
상기의 코딩은 벌써 이해되고 있다고 생각합니다. 그러나, 가독성이라고 하는 의미에서는 어떻습니까?  적어도 「12330/86400」은 코딩 한 본인이라도 시간이 지나면 기억이 날라나 몰겠네요.
「어짜피 내가 유지보수 안 할테니까 대충 짜지머...」란 생각은 하지 맙시다..
이 업계에는
 
「3일 경과하면 타인의 코드」란 격언이 있습니다.
 
읽기 쉬운 코드에 불평하는 사람은 없습니다(아마).
 
그런데 이러한 SQL가 Oracle9i로부터 추가된 기간 데이터형을 이용하면 어떻게 되는지……, 그 전에 기간 데이터형의 일람을 적어 둡시다.기간 데이터형으로서는 이하와 같은 지정 방법이 있습니다.
 
INTERVAL 'n' YEAR
INTERVAL 'n' MONTH
INTERVAL 'n' DAY
INTERVAL 'n' MINUTE
INTERVAL 'n' SECOND
INTERVAL 'y-m' YEAR TO MONTH
INTERVAL 'd hh24:mi:ss.ff' DAY TO SECOND
리스트 3 기간 데이터형의 지정 방법
 
    또, 문자열의 기간 데이터형에의 변환 함수도 있습니다.
 
TO_DSINTERVAL
TO_YMINTERVAL
리스트 4 문자열의 기간 데이터형에의 변환 함수
 
    그럼 이것들을 사용해 앞의 SQL를 고쳐 써 봅시다.우선은, 일자 포맷을 보기 쉽게 변경하고 나서 확인합니다.
 
SQL> ALTER SESSION
       SET nls_date_format = 'yyyy/mm/dd hh24:mi:ss';

SQL> SELECT
       SYSDATE,
       SYSDATE - (3*1/24+25*1/24/60 + 30*1/24/60/60)
         TARGET_DATE
     FROM dual;

SYSDATE             TARGET_DATE
------------------- -------------------
2005/04/08 02:38:30 2005/04/07 23:13:00
리스트 5 SQL(리스트 1)
 
SQL> SELECT
       SYSDATE ,
       SYSDATE - TO_DSINTERVAL('0 03:25:30') TARGET_DATE
     FROM dual;

SYSDATE             TARGET_DATE
------------------- -------------------
2005/04/08 02:40:23 2005/04/07 23:14:53
리스트 6 기간 데이터형에 의한 연산( 1)
 
    또는
 
SQL> SELECT
       SYSDATE ,
       SYSDATE - INTERVAL '0 03:25:30' DAY TO SECOND
         TARGET_DATE
     FROM dual;

SYSDATE             TARGET_DATE
------------------- -------------------
2005/04/08 02:41:33 2005/04/07 23:16:03
리스트 7 기간 데이터형에 의한 연산( 2)
 
어떻습니까. 기간 데이터형을 사용하면, 심플하고 가독성의 높은 SQL가 되었다고 생각하지 않습니까. 이 함수를 사용하면 시분초를 그대로 쓸 수 있습니다.
 
 
기간 데이터형의 년월 연산

그런데 1/24등으로 DATE형의 데이터의 「일」과「시분초」를 계산할 수 있는 것은 전회에 해설했던 대로입니다.그러나 달의 계산은1/24방식으로는 대응하지 못하고 ADD_MONTHS 함수를 사용했습니다.
실은 기간 데이터형의 경우도, 세월 연산용의 함수나 연산자가 별도 준비되어 있습니다.이것을 사용하는 것으로, 방금전과 같게 가독성의 높은 기술이 가능하게 됩니다.
그럼 기간 데이터형을 이용하고, 현재 시각부터 1년 6개월 후의 일자를 취득해 봅시다.
SQL> SELECT
       SYSDATE,
       SYSDATE + TO_YMINTERVAL('1-6') TARGET_DATE
     FROM dual;

SYSDATE             TARGET_DATE
------------------- -------------------
2005/04/08 02:47:29 2006/10/08 02:47:29
리스트8 기간데이터형에 의한 년월연산(1)

SQL> SELECT
       SYSDATE,
       SYSDATE + INTERVAL '1-6' YEAR TO MONTH TARGET_DATE
     FROM dual;

SYSDATE             TARGET_DATE
------------------- -------------------
2005/04/08 02:48:39 2006/10/08 02:48:39
리스트9 기간데이터형에 의한 년월연산(2)
   
    물론 세월과 일자 시각을 한번에 바꾸는것도 가능합니다.
 
SQL> SELECT
       SYSDATE,
       SYSDATE + INTERVAL '1-6' YEAR TO MONTH
               + INTERVAL '10' DAY TARGET_DATE
     FROM dual;

SYSDATE             TARGET_DATE
------------------- -------------------
2005/04/08 02:50:15 2006/10/18 02:50:15
리스트 10 세월과 일자 시각의 계산을 조합한다
 
가독성이 높다고 하는 것은 디버그의 효율화라고 하는 점에 대해서도 메리트가 있습니다.꼭 시도해 보셨으면 하는 테크닉입니다.
 
SQL를 쓰기 시작해서  얼마 되지 않은 프로그래머 분들은 일자 데이터에 관한 연산에 대해 조금은 정리할 수 있었는지요.또, 일자 데이터의 연산은 간단·간단이라고 생각되고 있던 여러분, 기간 데이터형을 사용한 연산은 이해되었습니까.
 
일자 데이터의 연산에 대해서는 여기까지입니다. 함수의 사용법에 대해서는, 상세한 설명이 오라클사의 메뉴얼에 기재되어 있기 때문에 이 기사의 내용이 이해된 분들은 재차 메뉴얼을 확인해 보시길..바랍니다.
자아......다음 연제는 Oracle9i로부터 도입된 타임 존에 대해 해설할 예정입니다
 
 
출처 블로그 > KACHISORI
원본 http://blog.naver.com/hirokorea/20028413598
본 자료는 일본 @IT(http://www.atmarkit.co.jp/fdb/index/index-db.html#sqlclinic)에 株式会社インサイトテクノロジー의 大道隆久씨가 연재한 SQLクリニック를 번역 재구성한 것입니다.
 
일자 데이터에 관한 화두로 「일자 데이터 연산의 달인기술을 전수한다」는 이번에 3번째입니다.마지막으로 타임 존에 관한 언급한후 매듭을 짓겠습니다.
 
데이타베이스에 있어서의 타임 존

타임 존라는 말 자체에 대해서는 아마도 어떠한 이미지가 있다고 생각합니다. 여기에서는 데이타베이스로 이용되는 간단한 예를 이야기합니다.너무 심플해서 현실에 입각해서 매칭되는 면이 없을지도 모르겠습니다만 다음과 같은 케이스를 생각합니다.
 
미국으로부터 발송한 부품과 싱가폴로부터 발송한 부품을 일본에서 조립하고, 그 후 완성품으로서 영국에 보낸다고 하는 스토리입니다.
그림을 그려보자면..
 
            미국----┐
                        │─── 일본 ─── 영국
            싱가폴--┘
            그림 1 복수의 나라에 걸친 비즈니스 플로우
 
그럼 이것들에 시각을 적용시키고, 최종적으로 영국에는 언제 닿는지를 봅시다.
 
전제조건
  미국에서 일본으로 수송에 12시간
  싱가폴에서 일본으로 수송에 8시간
  일본에서의 조립에 24시간
  일본에서 영국으로 수송에 20시간
 
부품의 발송 상황을 확인해 봅시다.데이타베이스 자체는 일본에 있고 모든 정보는 이 데이타베이스에 있다고 합니다.이 데이타베이스를 검색하면 부품이 발송된 일시를 검색할 수 있습니다.
미국으로부터의 부품 발송 → 현지 시각의 1월 10일의 오전 11시
싱가폴로부터의 부품 발송 → 현지 시각의 1월 11일의 오후 4시
일본은 현재, 1월 11일의 오후 4시입니다.그런데, 일본에는 언제 부품이 모인 것입니까? 영국의 유저의 수중에는 몇 시경에 완성품이 도착하는 것입니까?
 
그림 2 타임 존을 고려하지 않는 데이타베이스 설계( 각 로컬 타임을 데이터로서 보관 유지한다)
 
미국과 싱가폴로부터 부품이 발송된 시각이 등록되어 있습니다.일본에서의 조립 작업은 나중에 도착할 부품을 기다렸다가 개시될 것입니다. 어느 쪽의 부품이 다음에 도착하는 걸까요.? 언뜻 보는 것으로는 알기 힘들네요.
일자 데이터가 각지의 시각에 등록되어 있기 위해서, 각각의 시각의 관련을 이해하기 어렵게하고 있습니다. 그럼 이것들을 모두 일본 시각으로 변환하고 생각해 봅시다.
 
미국에서 부품발송
일본 시각 1월 11일의 오전 4시
싱가폴로부터의 부품 발송
일본 시각 1월 11일의 오후 5시
 
그 말은, 미국으로부터 부품이 도착하는 것은, 12시간 후의 1월 11일의 오후 4시.싱가폴로부터 부품이 도착하는 것은, 8시간 후의 1월 12일의 오전 1시(미국으로부터의 부품은 벌써 와있겠네요.)
즉 싱가폴로부터의 부품이 나중에 도착할 것입니다.그리고 조립을 개시하고, 완성하는 것이 24시간 후의 1월 13일의 오전 1시.그 말은 영국에 도착하는 것이 그리고 20시간 후의, 1월 13일의 오후 9시가 됩니다.현지 시각에 1월 13일의 정오군요.
 
상기와 같이 시각 계산을 단일의 타임 존으로 실시해, 검색 결과의 표시에 대해서는 유저의 타임 존에 응한 표시를 할 수 있으면 매우 알기 쉽고 편리합니다.
 
서론이 길어졌습니다만, 이것을 실현해 주는 기능이 타임 존입니다.
상기의 경우, 데이타베이스·타임 존이 일본 시각에 마춰져, 각 유저가 확인하는 시각은 각각의 세션·타임 존으로 확인하고 있습니다.
 
그림 3 타임 존을 고려한 데이타베이스 설계( 각 타임 존을 고려해 데이터를 보관 유지한다)
 
상기와 같이 타임 존을 사용하는 경우의 구체인 설정에 대하고 예를 나타냅니다.주의해야 할 점은, 이하의 3점입니다.
  1. 데이타베이스·타임 존(注1):"Japan"
  2. 세션·타임 존(注2):각 클라이언트의 지역의 타임 존
  3. 시각 데이터를 모두 TIMESTAMP WITH LOCAL TIME ZONE형의 열에 격납
상기의 데이타베이스·타임 존에 대해 오라클사의 추천은 「UTC」같습니다만.
 
타임 존의 종류

주1
  데이타베이스·타임 존
CREATE DATABASE 시에 결정하는 문자 그대로 데이타베이스의 타임 존입니다.이 타임 존은, 디폴트는 OS의 타임 존으로 설정됩니다.이 타임 존이 영향을 주는 것은 TIMESTAMP WITH LOCAL TIME ZONE형의 열에 격납되고 있는 데이터입니다.

주2 세션·타임 존
데이타베이스에 접속하는 세션 마다 타임 존의 설정이 가능합니다.세션·타임 존은 클라이언트측의 환경 변수 ORA_SDTZ로 설정됩니다만, 이 환경 변수가 정의되어 있지 않은 경우는, 데이타베이스·타임 존이 디폴트로서 사용됩니다.유저의 지역에 의해서 시각을 현지 시각에 맞추어 표시시키는 경우는, 이 타임 존이 영향을 줍니다.
즉 Web 서버나 어플리케이션 서버를 구성의 경우는, 세션·타임 존이 Web 서버나 어플리케이션 서버의 세션에 대해서 유효하게 되기 때문에 최종 사용자의 환경과는 관계없게 되어져 효과가 없습니다.

INSERT 할 때도, SELECT 할 때도, 어플리케이션측에서는 특히 타임 존을 의식할 필요는 없습니다.물론 서버의 타임 존을 포함해 타임 존에 관한 설정이 올바르다는 것이 대전제입니다.
 
 그림 4 세션·타임 존과 데이타베이스·타임 존
 
TIMESTAMP WITH LOCAL TIME ZONE형의 열은 유저 환경과는 관계없이 모두 데이타베이스·타임 존에 변환된 일시 정보가 격납됩니다. 그래서온 세상의 일자 시각의 데이터를 TIMESTAMP WITH LOCAL TIME ZONE형의 열에 격납하면, 필연적으로 단일의 타임 존으로 데이터를 취급하게 됩니다.
게다가 TIMESTAMP WITH LOCAL TIME ZONE형의 열에 격납된 데이터는, 유저 세션의 타임 존에 따라서 변환되어 클라이언트 측에 표시됩니다.
 
상기 외에도 타임 존의 정보를 격납할 수 있는 데이터형이 있습니다.TIMESTAMP WITH TIME ZONE형입니다. 이것은 데이터를 넣은 어플리케이션(세션)의 시각과 그 타임 존 정보가 격납됩니다.이 데이터를 검색하면, 자신의 타임 존(검색한 세션의 타임 존)과는 관계없이, 데이타베이스에 격납되고 있는 일시와 타임 존을 그대로 검색할 수 있습니다.필요에 따라서 구분하여 하시면 됩니다.
 
타임 존의 확인

현재의 타임 존의 설정은 아래와 같은 SQL로 확인할 수 있습니다.
 
SQL> SELECT dbtimezone FROM dual;

DBTIME
------
+00:00
리스트 1 데이타베이스·타임 존의 확인

SQL> SELECT sessiontimezone FROM dual;

SESSIONTIMEZONE
---------------
GMT
리스트 2 데이타베이스·타임 존의 확인
 
상기의 「+00:00」과「GMT」는 함께 그리니치 표준시와 같은 의미입니다. 설정의 방법으로 표시 방법이 바뀌는 예를 들어 보았습니다.타임 존은 CREATE DATABASE문으로 설정한다, 혹은 후술의 ALTER DATABASE문으로 변경할 때에 타임 존명으로 설정하거나 오프셋치로 설정으로 바뀝니다.
 
그런데, TIME ZONE 정보는 어떻게 표시되는가 하면, 다음과 같은 표시가 됩니다.
 
SQL> SELECT systimestamp FROM dual;

SYSTIMESTAMP
-------------------------------
05-05-17 04:26:23.476000 +09:00
리스트 3 TIME ZONE 정보의 표시 위치
 
오른쪽의 「+09:00」이 타임 존을 나타내고 있습니다.
 
SYSTIMESTAMP 함수로 취득되는 정보는 서버의 시각을 나타내고 있기 때문에, 세션·타임 존이나 데이타베이스·타임 존을 변경해도 변화는 없습니다.이것은 SYSDATE 함수에 대해서도 같습니다.
 
타임 존의 변경

타임 존은 변경할 수 있습니다. 현재의 타임 존을 확인했는데 「설계 시점의 타임 존과 다르다.전부 다시 만들어야 겠다―」라고 포기하기 전에, 이하의 ALTER문으로 변경할 수 없는지 어떤지 확인해 보세요.
 
데이터베이스 타임 존의 변경
통상 CREATE DATABASE 시에 결정합니다만, 조건(주 3)을 만족하는 경우는 데이타베이스 작성 후에도 변경가능 합니다.
 
SQL> ALTER database SET time_zone = 'US/Arizona';
  또는
SQL> ALTER database SET time_zone = '-08:00';

세션 타임 존 변경
SQL> ALTER session SET time_zone = 'US/Arizona';
  또는
SQL> ALTER session SET time_zone = '-08:00';

데이터베이스 타임 존 변경의 조건
주 3 데이타베이스내에 TIMESTAMP WITH LOCAL TIME ZONE형의 열을 가지는 테이블이 존재하는 경우는 데이타베이스·타임 존의 변경은 할 수 없습니다.이것은 TIMESTAMP WITH LOCAL TIME ZONE형의 데이터가 데이타베이스·타임 존에 따라서 데이터를 격납하고 있기 때문입니다.즉 데이타베이스·타임 존을 변경하는 경우는 TIMESTAMP WITH LOCAL TIME ZONE형의 열을 가지는 테이블을 모두 DROP 하거나 TIMESTAMP WITH LOCAL TIME ZONE형의 열을 모두 DROP 해야 합니다.데이터가 0건에서도 에러가 납니다.……그러므로  TRUNCATE로 대충 하려고 해도 안된답니다.

또, 데이타베이스·타임 존은 ALTER DATABASE문으로 변경 후에 인스턴스를 재기동하지 않으면 반영되지 않기 때문에 주의!@
 
참고로 다른 세션·타임 존으로 데이터형이 다르다고 표시가 어떻게 다를까 봐 둡시다.
 
SQL> SELECT dbtimezone FROM dual;

DBTIME
------
+09:00

SQL> SELECT sessiontimezone FROM dual;

SESSIONTIMEZONE
---------------
+09:00

SQL> CREATE TABLE hogehoge (
  2   c1 timestamp(6)
  3  ,c2 timestamp(6) with time zone
  4  ,c3 timestamp(6) with local time zone
  5  );

Table created.

SQL> INSERT INTO hogehoge (
  2   c1
  3  ,c2
  4  ,c3
  5  ) values (
  6   to_timestamp('2005/05/14 12:34:56.123456', 'yyyy/mm/dd hh24:mi:ss.ff')
  7  ,to_timestamp('2005/05/14 12:34:56.123456', 'yyyy/mm/dd hh24:mi:ss.ff')
  8  ,to_timestamp('2005/05/14 12:34:56.123456', 'yyyy/mm/dd hh24:mi:ss.ff')
  9  );

1 row created.

SQL> commit;

Commit complete.

SQL> SELECT * FROM hogehoge;

C1                       C2                              C3
------------------------ ------------------------------- ------------------------
05-05-14 12:34:56.123456 05-05-14 12:34:56.123456 +09:00 05-05-14 12:34:56.123456
리스트 4 다른 데이터형으로 일자 데이터를 격납
 
같은 데이터를 다른 타임 존으로 확인하면, 리스트 5와 같이 됩니다.
 
SQL> ALTER session SET time_zone = 'US/Arizona';

Session altered.

SQL> SELECT * FROM hogehoge;

C1                       C2                              C3
------------------------ ------------------------------- ------------------------
05-05-14 12:34:56.123456 05-05-14 12:34:56.123456 +09:00 05-05-13 20:34:56.123456
리스트 5 같은 데이터를 다른 타임 존으로 취득
 
C1열(TIMESTAMP형)에는 변화가 없습니다. 즉 타임 존의 변화가 고려되어 있지 않게 됩니다.
C2열(TIMESTAMP WITH TIME ZONE형)도 변화가 없습니다. 하지만, 원래 데이터로서 타임 존의 정보가 포함되어 있기 때문에, 그 평가는 가능합니다.
C3열(TIMESTAMP WITH LOCAL TIME ZONE형)은 시각이 변화하고 있습니다.데이타베이스의 처리로서 세션·타임 존을 반영해 변환되고 있는 것을 알수 있습니다.
 
타임 존 데이터의 export/import

 
그럼 타임 존의 대략적인 이미지를 잡으셨다면 타임 존 정보를 포함한 데이터의 export/임포트에 대해 주의점을 설명합니다. 특히 타임 존이 다른 데이터 베이스 서버간에 데이터를 export/임포트 하는 경우입니다.
결론부터 말하면, TIMESTAMP WITH (LOCAL) TIME ZONE형의 데이터에 대해서는 문제 없이 임포트 할 수 있습니다. TIMESTAMP형과 DATE형의 데이터도 물론 문제 없이 임포트 할 수 있습니다……하지만, TIMESTAMP형과 DATE형은 타임 존을 전혀 의식하지 않는 상태로 데이타베이스에 격납되고 있기 때문에 충분히 주의하지 않으면 새로운 타임 존으로 구데이터 베이스 서버의 시각을 평가해 버리는 등, 정합성을 떨어질 가능성이 있습니다.
TIMESTAMP WITH TIME ZONE형은 데이터로서 타임 존을 보관 유지하고 있기 때문에 타임 존이 다른 데이터 베이스 서버에 임포트 해도 타임 존을 무시하는 평가가 가능합니다.
복수의 일시 데이터형이 혼재하고 있는 시스템에서는 이런 것들에 충분히 주의해 운용할 필요가 있습니다.
 
이번회를 포함한 3회에 걸쳐 일자와 타임 존의 연산이나 조작의 방법에 대해 대충 해설해 왔습니다.어떻게였다입니까.새로운 것을 기억하지 않아도 「SYSDATE와1/24방식으로 완성되기 때문에 상관 없잖아」라고 하는 분도 계시겠지요. 그러나, 가독성이 높아진다란 메리트도 크다 라고 생각이 들고 무엇보다 기간 데이터형을 사용한 연산은 사용해 보심이 어떠실란지....
 
타임 존에 대해서는 지금 당장 사용할 필요가 없을지도 모릅니다. 특히 해외와의 제휴가 원래 없는 것 같으면 그것도 당연합니다. 단지, 새로운 시스템이 해외와의 데이터 제휴의 가능성이 있는 경우는, 미리 대처할 수 있도록 해 두지 않으면 export/임포트에 한정하지 않더라도 귀찮은 것이 될지도 모릅니다.
 
너무 타임 존을 의식하는 것이 없지 않았나 하는 생각도 들지만 조금 주위를 기울이면 세계가 의외로 근처에 있는 것을 재차 인식하는 좋을 기회가 되는 것은 아닐까요. 다음 번은, 집계 함수의 편리한 사용법에 대해 소개합니다.
 
출처 : Naver, cinqueterre 님 블로그
Posted by Huikyun