Oracle/SQL2014. 5. 15. 00:48

Time Dimension Table을 생성해보자.

 

아래와 같이 td 테이블을 생성하자. 성능을 고려하여 cyl_cd 칼럼을 파티션 키로 가진 List Partitioned IOT로 생성했다.

-- 1
DROP TABLE td PURGE;

CREATE TABLE td (
    cyl_cd   VARCHAR(1)  -- 주기코드
  , std_dt   VARCHAR(8)  -- 기준일자
  , bgn_dt   VARCHAR(8)  -- 시작일자
  , end_dt   VARCHAR(8)  -- 종료일자
  , std_dt_w VARCHAR(6)  -- 기준일자_주간
  , std_dt_m VARCHAR(6)  -- 기준일자_월간
  , std_dt_q VARCHAR(6)  -- 기준일자_분기
  , std_dt_h VARCHAR(6)  -- 기준일자_반기
  , std_dt_y VARCHAR(4)  -- 기준일자_연간
  , out_dt   VARCHAR(10) -- 출력일자
  , CONSTRAINT td_pk PRIMARY KEY (cyl_cd, std_dt)
  , CONSTRAINT td_c1 CHECK (bgn_dt <= end_dt))
ORGANIZATION INDEX
PCTFREE 0
PARTITION BY LIST (cyl_cd) (
    PARTITION p_d VALUES ('D')
  , PARTITION p_w VALUES ('W')
  , PARTITION p_m VALUES ('M')
  , PARTITION p_q VALUES ('Q')
  , PARTITION p_h VALUES ('H')
  , PARTITION p_y VALUES ('Y'));

CREATE UNIQUE INDEX td_u1 ON td (cyl_cd, end_dt, bgn_dt, std_dt) LOCAL PCTFREE 0;

 

기준일자 값은 Fact Table의 Time Dimension 칼럼 값으로 생성하면 되고, 출력일자 값은 UI 표준을 따라 생성하면 된다. 주기별 기준일자 칼럼(std_dt_%)은 주기간의 시계열을 연결하기 위해 사용된다.

-- 2
INSERT
  INTO td
      (cyl_cd, std_dt, bgn_dt, end_dt, std_dt_w, std_dt_m, std_dt_q, std_dt_h, std_dt_y, out_dt)
WITH w1 AS
     (SELECT DATE '1950-01-01' AS bgn_dt
           , DATE '2051-01-01' AS end_dt
        FROM DUAL)
   , w1_d AS
     (SELECT     'D' AS cyl_cd
               , TO_CHAR (a.bgn_dt + LEVEL - 1, 'YYYYMMDD') AS std_dt
               , TO_CHAR (a.bgn_dt + LEVEL - 1, 'YYYYMMDD') AS bgn_dt
               , TO_CHAR (a.bgn_dt + LEVEL - 1, 'YYYYMMDD') AS end_dt
               , TO_CHAR (a.bgn_dt + LEVEL - 1, 'IYYYIW') AS std_dt_w
               , TO_CHAR (a.bgn_dt + LEVEL - 1, 'YYYYMM') AS std_dt_m
               , TO_CHAR (a.bgn_dt + LEVEL - 1, 'YYYY')
                 || LPAD (CEIL (TO_CHAR (a.bgn_dt + LEVEL - 1, 'MM') / 3) * 3, 2, '0') AS std_dt_q
               , TO_CHAR (a.bgn_dt + LEVEL - 1, 'YYYY')
                 || LPAD (CEIL (TO_CHAR (a.bgn_dt + LEVEL - 1, 'MM') / 6) * 6, 2, '0') AS std_dt_h
               , TO_CHAR (a.bgn_dt + LEVEL - 1, 'YYYY') AS std_dt_y
               , TO_CHAR (a.bgn_dt + LEVEL - 1, 'YYYY-MM-DD') AS out_dt
            FROM w1 a
      CONNECT BY LEVEL <= a.end_dt - a.bgn_dt)
   , w1_w AS
     (SELECT   'W' AS cyl_cd
             , TO_CHAR (TO_DATE (a.std_dt, 'YYYYMMDD'), 'IYYYIW') AS std_dt
             , MIN (a.bgn_dt)   AS bgn_dt  , MAX (a.end_dt)   AS end_dt
             , MAX (a.std_dt_w) AS std_dt_w, MAX (a.std_dt_m) AS std_dt_m
             , MAX (a.std_dt_q) AS std_dt_q, MAX (a.std_dt_h) AS std_dt_h
             , MAX (a.std_dt_y) AS std_dt_y
             , TO_CHAR (TO_DATE (MAX (a.std_dt), 'YYYYMMDD'), 'IYYY-IW"W"') AS out_dt
          FROM w1_d a
      GROUP BY TO_CHAR (TO_DATE (a.std_dt, 'YYYYMMDD'), 'IYYYIW'))
   , w1_m AS
     (SELECT   'M' AS cyl_cd
             , SUBSTR (a.std_dt, 1, 6) AS std_dt
             , MIN (a.bgn_dt)   AS bgn_dt  , MAX (a.end_dt)   AS end_dt
             , MAX (a.std_dt_w) AS std_dt_w, MAX (a.std_dt_m) AS std_dt_m
             , MAX (a.std_dt_q) AS std_dt_q, MAX (a.std_dt_h) AS std_dt_h
             , MAX (a.std_dt_y) AS std_dt_y
             , TO_CHAR (TO_DATE (MAX (a.std_dt), 'YYYYMMDD'), 'YYYY-MM') AS out_dt
          FROM w1_d a
      GROUP BY SUBSTR (a.std_dt, 1, 6))
   , w1_q AS
     (SELECT   'Q' AS cyl_cd
             , MAX (SUBSTR (a.std_dt, 1, 6)) AS std_dt
             , MIN (a.bgn_dt)   AS bgn_dt  , MAX (a.end_dt)   AS end_dt
             , MAX (a.std_dt_w) AS std_dt_w, MAX (a.std_dt_m) AS std_dt_m
             , MAX (a.std_dt_q) AS std_dt_q, MAX (a.std_dt_h) AS std_dt_h
             , MAX (a.std_dt_y) AS std_dt_y
             , TO_CHAR (TO_DATE (MAX (a.std_dt), 'YYYYMM'), 'YYYY-Q"Q"') AS out_dt
          FROM w1_m a
      GROUP BY TO_CHAR (TO_DATE (a.std_dt, 'YYYYMM'), 'YYYYQ'))
   , w1_h AS
     (SELECT   'H' AS cyl_cd
             , MAX (SUBSTR (a.std_dt, 1, 6)) AS std_dt
             , MIN (a.bgn_dt)   AS bgn_dt  , MAX (a.end_dt)   AS end_dt
             , MAX (a.std_dt_w) AS std_dt_w, MAX (a.std_dt_m) AS std_dt_m
             , MAX (a.std_dt_q) AS std_dt_q, MAX (a.std_dt_h) AS std_dt_h
             , MAX (a.std_dt_y) AS std_dt_y
             , SUBSTR (a.std_dt, 1, 4) || '-' || CEIL (SUBSTR (a.std_dt, 5, 2) / 6) || 'H' AS out_dt
          FROM w1_q a
      GROUP BY SUBSTR (a.std_dt, 1, 4)
             , CEIL (SUBSTR (a.std_dt, 5, 2) / 6))
   , w1_y AS
     (SELECT   'Y' AS cyl_cd
             , SUBSTR (a.std_dt, 1, 4) AS std_dt
             , MIN (a.bgn_dt)   AS bgn_dt  , MAX (a.end_dt)   AS end_dt
             , MAX (a.std_dt_w) AS std_dt_w, MAX (a.std_dt_m) AS std_dt_m
             , MAX (a.std_dt_q) AS std_dt_q, MAX (a.std_dt_h) AS std_dt_h
             , MAX (a.std_dt_y) AS std_dt_y
             , SUBSTR (a.std_dt, 1, 4) AS out_dt
          FROM w1_h a
      GROUP BY SUBSTR (a.std_dt, 1, 4))
SELECT   a.* FROM w1_d a UNION ALL
SELECT   a.* FROM w1_w a UNION ALL
SELECT   a.* FROM w1_m a UNION ALL
SELECT   a.* FROM w1_q a UNION ALL
SELECT   a.* FROM w1_h a UNION ALL
SELECT   a.* FROM w1_y a
ORDER BY 1, 2;

COMMIT;

EXEC DBMS_STATS.GATHER_TABLE_STATS (ownname => 'HRJEONG', tabname => 'td', cascade => TRUE);

 

아래는 cyl_cd 별로 td 테이블을 조회한 결과다.

-- 3-1 : daily
SELECT a.*
  FROM td a
 WHERE a.cyl_cd = 'D'
   AND a.std_dt BETWEEN '20140101' AND '20140104';

CYL_CD STD_DT   BGN_DT   END_DT   STD_DT_W STD_DT_M STD_DT_Q STD_DT_H STD_DT_Y OUT_DT
------ -------- -------- -------- -------- -------- -------- -------- -------- ----------
D      20140101 20140101 20140101 201401   201401   201403   201406   2014     2014-01-01
D      20140102 20140102 20140102 201401   201401   201403   201406   2014     2014-01-02
D      20140103 20140103 20140103 201401   201401   201403   201406   2014     2014-01-03
D      20140104 20140104 20140104 201401   201401   201403   201406   2014     2014-01-04

4 행이 선택되었습니다.

-- 3-2 : weekly
SELECT *
  FROM td a
 WHERE a.cyl_cd = 'W'
   AND a.std_dt BETWEEN '201401' AND '201404';

CYL_CD STD_DT   BGN_DT   END_DT   STD_DT_W STD_DT_M STD_DT_Q STD_DT_H STD_DT_Y OUT_DT
------ -------- -------- -------- -------- -------- -------- -------- -------- --------
W      201401   20131230 20140105 201401   201401   201403   201406   2014     2014-01W
W      201402   20140106 20140112 201402   201401   201403   201406   2014     2014-02W
W      201403   20140113 20140119 201403   201401   201403   201406   2014     2014-03W
W      201404   20140120 20140126 201404   201401   201403   201406   2014     2014-04W

4 행이 선택되었습니다.

-- 3-3 : monthly
SELECT a.*
  FROM td a
 WHERE a.cyl_cd = 'M'
   AND a.std_dt BETWEEN '201401' AND '201404';

CYL_CD STD_DT   BGN_DT   END_DT   STD_DT_W STD_DT_M STD_DT_Q STD_DT_H STD_DT_Y OUT_DT
------ -------- -------- -------- -------- -------- -------- -------- -------- -------
M      201401   20140101 20140131 201405   201401   201403   201406   2014     2014-01
M      201402   20140201 20140228 201409   201402   201403   201406   2014     2014-02
M      201403   20140301 20140331 201414   201403   201403   201406   2014     2014-03
M      201404   20140401 20140430 201418   201404   201406   201406   2014     2014-04

4 행이 선택되었습니다.

-- 3-4 : quarterly
SELECT a.*
  FROM td a
 WHERE a.cyl_cd = 'Q'
   AND a.std_dt BETWEEN '201401' AND '201412';

CYL_CD STD_DT   BGN_DT   END_DT   STD_DT_W STD_DT_M STD_DT_Q STD_DT_H STD_DT_Y OUT_DT
------ -------- -------- -------- -------- -------- -------- -------- -------- -------
Q      201403   20140101 20140331 201414   201403   201403   201406   2014     2014-1Q
Q      201406   20140401 20140630 201427   201406   201406   201406   2014     2014-2Q
Q      201409   20140701 20140930 201440   201409   201409   201412   2014     2014-3Q
Q      201412   20141001 20141231 201501   201412   201412   201412   2014     2014-4Q

4 행이 선택되었습니다.

-- 3-5 : half-yearly
SELECT a.*
  FROM td a
 WHERE a.cyl_cd = 'H'
   AND a.std_dt BETWEEN '201401' AND '201512';

CYL_CD STD_DT   BGN_DT   END_DT   STD_DT_W STD_DT_M STD_DT_Q STD_DT_H STD_DT_Y OUT_DT
------ -------- -------- -------- -------- -------- -------- -------- -------- -------
H      201406   20140101 20140630 201427   201406   201406   201406   2014     2014-1H
H      201412   20140701 20141231 201501   201412   201412   201412   2014     2014-2H
H      201506   20150101 20150630 201527   201506   201506   201506   2015     2015-1H
H      201512   20150701 20151231 201553   201512   201512   201512   2015     2015-2H

4 행이 선택되었습니다.

-- 3-6 : yearly
SELECT a.*
  FROM td a
 WHERE a.cyl_cd = 'Y'
   AND a.std_dt BETWEEN '2014' AND '2017';

CYL_CD STD_DT   BGN_DT   END_DT   STD_DT_W STD_DT_M STD_DT_Q STD_DT_H STD_DT_Y OUT_DT
------ -------- -------- -------- -------- -------- -------- -------- -------- ------
Y      2014     20140101 20141231 201501   201412   201412   201412   2014     2014
Y      2015     20150101 20151231 201553   201512   201512   201512   2015     2015
Y      2016     20160101 20161231 201652   201612   201612   201612   2016     2016
Y      2017     20170101 20171231 201752   201712   201712   201712   2017     2017

4 행이 선택되었습니다.


'Oracle > SQL' 카테고리의 다른 글

선분 이력 분리  (0) 2014.05.15
시계열 채우기  (0) 2014.05.15
다중 열 다중 행 스칼라 서브 쿼리  (0) 2014.05.14
파티션 확장 절  (0) 2014.05.10
Native LEFT OUTER JOIN 동작 개선  (0) 2014.04.19
Posted by 정희락_