關於Oracle中分區表Partition與引用型遊標ref cursor的應用範例

--公司最近作數據集市DM建模 我把我這部分貼出來供你們參考 還請批評指正2016-02-22函數

--本文涉及Oracle的基礎語法等再也不贅述spa

DECLARE
  T_TABLE_NAME varchar2(20) := 'DM_R3G_$YYYYMM'; --表名DM_R3G_INFO_$YYYYMM
  T_TIMEST     VARCHAR2(8) := '20160218'; --20160218
  T_OPER_NO    VARCHAR2(20); --申請人工號
  T_IP         VARCHAR2(20); --申請人IP 127.0.0.1

  IS_EXISTS     NUMBER; --分區表是否存在
  T_YEAR        VARCHAR2(4); --
  T_MONTH       VARCHAR2(2); --
  T_DAY         VARCHAR2(2); --
  T_YEAR_MONTH  VARCHAR2(6); --
  T_DATA_SOURCE VARCHAR2(50); --R3G_INFO_DAY@LINK_FSDB
  T_DATA_FILTER VARCHAR2(100); --TIMEST = TO_DATE('$YYYYMMDD', 'YYYYMMDD')
  T_CREATE_MODE VARCHAR2(10); --獲取模式 003-年按月分區 004-月按日分區

  T_PARTITION_NUM NUMBER;

  type TYPE_CUR IS REF CURSOR; --用引用遊標添加分區表DM_R3G_INFO_201602的columns
  CUR_TABLE_COLS TYPE_CUR;
  T_COLUMN_NAME  ALL_TAB_COLUMNS.COLUMN_NAME%TYPE;--引用類型
  T_DATA_TYPE    ALL_TAB_COLUMNS.DATA_TYPE%TYPE;
  T_DATA_LENGTH  ALL_TAB_COLUMNS.DATA_LENGTH%TYPE;
  T_COL_INFO     VARCHAR2(50);

  IS_COL_IN_TABLE NUMBER := 0;
  T_INSERT_COLS   VARCHAR2(8000);
BEGIN
  --第一步
  --SELECT * FROM DM_CODE_TABLE A WHERE A.TABLE_NAME = 'DM_R3G_INFO_$YYYYMM';
  BEGIN
    SELECT DATA_SOURCE, DATA_FILTER, CREATE_MODE
      INTO T_DATA_SOURCE, T_DATA_FILTER, T_CREATE_MODE
      FROM DM_CODE_TABLE@fs_dbcenter T
     WHERE T.TABLE_NAME = T_TABLE_NAME;
  EXCEPTION
    WHEN OTHERS THEN
      RAISE_APPLICATION_ERROR(-20000,'表名' || TRIM(T_TABLE_NAME) || '無效');
  END;

  --日期處理,把T_TIMEST轉成YYYYMM YYYY MM
  T_YEAR_MONTH := TO_CHAR(TO_DATE(T_TIMEST, 'YYYYMMDD'), 'YYYYMM');
  T_YEAR       := TO_CHAR(TO_DATE(T_TIMEST, 'YYYYMMDD'), 'YYYY');
  T_MONTH      := TO_CHAR(TO_DATE(T_TIMEST, 'YYYYMMDD'), 'MM');
  T_DAY        := TO_CHAR(TO_DATE(T_TIMEST, 'YYYYMMDD'), 'DD');
  --變量替換
  T_TABLE_NAME  := REPLACE(T_TABLE_NAME, '$YYYYMM', T_YEAR_MONTH); --DM_R3G_INFO_201602
  T_TABLE_NAME  := REPLACE(T_TABLE_NAME, '$YYYY', T_YEAR);
  T_TABLE_NAME  := REPLACE(T_TABLE_NAME, '$MM', T_MONTH);
  T_DATA_FILTER := REPLACE(T_DATA_FILTER, '$YYYYMMDD', T_TIMEST);
  T_DATA_FILTER := REPLACE(T_DATA_FILTER, '$YYYYMM', T_YEAR_MONTH);
  T_DATA_FILTER := REPLACE(T_DATA_FILTER, '$YYYY', T_YEAR);
  T_DATA_FILTER := REPLACE(T_DATA_FILTER, '$MM', T_MONTH);
  T_DATA_SOURCE := REPLACE(T_DATA_SOURCE, '$YYYYMM', T_YEAR_MONTH);
  T_DATA_SOURCE := REPLACE(T_DATA_SOURCE, '$YYYY', T_YEAR);
  T_DATA_SOURCE := REPLACE(T_DATA_SOURCE, '$MM', T_MONTH);

  --第二步 建立數據臨時表
  P_DROP_TABLE('TMP_' || T_TABLE_NAME);--封裝的函數,用途drop table XXX purge
  --拼SQL,把數據放臨時表
  EXECUTE IMMEDIATE 'CREATE TABLE TMP_' || T_TABLE_NAME ||
                    ' NOLOGGING AS
                    SELECT * FROM ' || T_DATA_SOURCE ||
                    ' A ' || (CASE
                      WHEN TRIM(T_DATA_FILTER) IS NOT NULL THEN
                       ' WHERE ' || TRIM(T_DATA_FILTER)
                      ELSE
                       NULL
                    END);
  
  --判斷建立模式
  
  --第三步:一、判斷表是否存在,若是不存在,建立一個空表先
  SELECT COUNT(1)
    INTO IS_EXISTS
    FROM ALL_TABLES A
   WHERE A.TABLE_NAME = T_TABLE_NAME
     AND A.OWNER = USER;
  
  --若是表不存在建立分區表
  IF IS_EXISTS = 0 THEN
    if T_CREATE_MODE = '003' THEN
      T_PARTITION_NUM := 12;
    ELSIF T_CREATE_MODE = '004' THEN
      T_PARTITION_NUM := 31;--模式003建立12個月分區,模式004建立31個日分區
    END IF;
    EXECUTE IMMEDIATE '
       CREATE TABLE ' || T_TABLE_NAME || '
          (
            P_ID              VARCHAR2(2)
          )
          PARTITION BY LIST (P_ID)
          (
              PARTITION ' || T_TABLE_NAME || '_P01 VALUES (''01'')
          )';--建立分區列P_ID
    FOR T_TMP_I IN 2 .. T_PARTITION_NUM LOOP
      EXECUTE IMMEDIATE '
              ALTER TABLE ' || T_TABLE_NAME ||
                        ' ADD PARTITION ' || T_TABLE_NAME || '_P' ||
                        LPAD(T_TMP_I, 2, '0') || ' VALUES(''' ||
                        LPAD(T_TMP_I, 2, '0') || ''')';
    END LOOP;--循環建立分區
    OPEN CUR_TABLE_COLS FOR 'SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH FROM ALL_TAB_COLUMNS A 
                             WHERE A.OWNER = ''' || user || ''' AND A.TABLE_NAME = ''TMP_' || T_TABLE_NAME || ''' AND COLUMN_NAME != ''P_ID''';
    LOOP
      EXIT WHEN CUR_TABLE_COLS%NOTFOUND;
      FETCH CUR_TABLE_COLS
        INTO T_COLUMN_NAME, T_DATA_TYPE, T_DATA_LENGTH; --IS_COL_IN_TABLE
      /*EXECUTE IMMEDIATE 'SELECT SUM(CASE WHEN COLUMN_NAME = ' ||
                        T_COLUMN_NAME ||
                        ' THEN 1 OTHERS THEN 0 END) 
                           WHERE A.OWNER = ''' || user ||
                        ''' AND A.TABLE_NAME = ' || T_TABLE_NAME ||
                        ' GROUP BY TABLE_NAME '
        INTO IS_COL_IN_TABLE;
      IF IS_COL_IN_TABLE = 0 THEN
        SELECT T_COLUMN_NAME || ' ' || T_DATA_TYPE ||
               DECODE(T_DATA_TYPE,
                      'VARCHAR2',
                      '(' || T_DATA_LENGTH || ')',
                      '')
          INTO T_COL_INFO
          FROM DUAL;
        EXECUTE IMMEDIATE 'ALTER TABLE ' || T_TABLE_NAME || ' ADD (' ||
                          T_COL_INFO || ')';
      ELSE
        NULL;
      END IF;*/ --不存在的列都插入,此爲拓展功能,若源表增長列時使用
      SELECT T_COLUMN_NAME || ' ' || T_DATA_TYPE ||
             DECODE(T_DATA_TYPE,
                    'VARCHAR2',
                    '(' || T_DATA_LENGTH || ')',
                    '')
        INTO T_COL_INFO
        FROM DUAL;
      EXECUTE IMMEDIATE 'ALTER TABLE ' || T_TABLE_NAME || ' ADD (' ||
                        T_COL_INFO || ')';
      --IS_COL_IN_TABLE := 0;
    END LOOP;
    CLOSE CUR_TABLE_COLS; --遊標結束,分區表的列已所有建立
  END IF;

  /*清空分區*/
  EXECUTE IMMEDIATE 'ALTER TABLE ' || T_TABLE_NAME ||
                    ' TRUNCATE PARTITION ' || T_TABLE_NAME || '_P' || (CASE
                      WHEN T_CREATE_MODE = '003' THEN
                       LPAD(T_DAY, 2, '0')
                      WHEN T_CREATE_MODE = '004' THEN
                       LPAD(T_MONTH, 2, '0')
                    END) || ' UPDATE GLOBAL INDEXES';--這裏必須更新全局索引,由於表的每一個分區能夠視爲獨立的表,都要更新索引

  SELECT WM_CONCAT(A.COLUMN_NAME)
    INTO T_INSERT_COLS
    FROM ALL_TAB_COLS A
   WHERE A.TABLE_NAME = 'TMP_' || T_TABLE_NAME
     AND A.OWNER = USER
     AND A.COLUMN_NAME != 'P_ID';--搜索全部列名,並concat鏈接後賦值給T_INSERT_COLS變量

  --正式保存數據
  execute immediate '
     INSERT INTO ' || T_TABLE_NAME || ' PARTITION(' ||
                    T_TABLE_NAME || '_P' || (CASE
                      WHEN T_CREATE_MODE = '003' THEN
                       LPAD(T_DAY, 2, '0')
                      WHEN T_CREATE_MODE = '004' THEN
                       LPAD(T_MONTH, 2, '0')
                    END) || ') NOLOGGING (P_ID, ' || T_INSERT_COLS || ')--此處注意NOLOGGING的位置,放在PARTITION後,列名前
      select ''' || (CASE
                      WHEN T_CREATE_MODE = '003' THEN
                       LPAD(T_DAY, 2, '0')
                      WHEN T_CREATE_MODE = '004' THEN
                       LPAD(T_MONTH, 2, '0')
                    END) || ''' P_ID, ' || T_INSERT_COLS || ' from TMP_' ||
                    T_TABLE_NAME;
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.put_LINE(SQLCODE || ':' || SQLERRM);
END;
相關文章
相關標籤/搜索