Oracle/PL/SQL2020. 2. 13. 14:57

예전 프로젝트에서 간단한 ETL 유틸리티 패키지를 작성했다.


패키지 사용법은 아래와 같다. 적재 전에 prc_begin_etl 프로시저를 수행하고, 적재 후에 prc_end_etl 프로시저를 수행하면 된다. 병렬도는 세그먼트 사이즈에 따라 자동으로 설정된다.

-- 1
fnc_get_partition_name    (i_owner, i_table_name, i_partition_key_value);
fnc_get_subpartition_name (i_owner, i_table_name, i_subpartition_key_value);

-- 2
prc_truncate_table        (i_owner, i_table_name);
prc_truncate_partition    (i_owner, i_table_name, i_partition_name);
prc_truncate_subpartition (i_owner, i_table_name, i_subpartition_name);

-- 3
prc_begin_etl (i_owner, i_table_name, i_partition_name);
    - prc_nologging_table    (i_owner, i_table_name);
    - prc_disable_constraint (i_owner, i_table_name);
    - prc_unusable_index     (i_owner, i_table_name, i_partition_name);

-- 4
INSERT

-- 5
prc_end_etl (i_owner, i_table_name, i_partition_name);
    - prc_rebuild_index     (i_owner, i_table_name, i_partition_name);
    - prc_enable_constraint (i_owner, i_table_name);
    - prc_logging_table     (i_owner, i_table_name);

-- 6
prc_gather_table_stats (i_owner, i_table_name, i_partition_name);

패키지 소스는 아래와 같다. 테스트 환경에서 검증 후 사용해야 한다.

CREATE OR REPLACE PACKAGE pkg_utl_etl
IS
    --------------------------------------------------
    -- pkg_utl_etl
    --------------------------------------------------
    FUNCTION fnc_get_partition_name (
        i_owner               IN VARCHAR2
      , i_table_name          IN VARCHAR2
      , i_partition_key_value IN VARCHAR2
    )
        RETURN VARCHAR2;

    FUNCTION fnc_get_subpartition_name (
        i_owner                  IN VARCHAR2
      , i_table_name             IN VARCHAR2
      , i_subpartition_key_value IN VARCHAR2
    )
        RETURN VARCHAR2;

    PROCEDURE prc_begin_etl (
        i_owner          IN VARCHAR2
      , i_table_name     IN VARCHAR2
      , i_partition_name IN VARCHAR2 DEFAULT NULL
    );

    PROCEDURE prc_end_etl (
        i_owner          IN VARCHAR2
      , i_table_name     IN VARCHAR2
      , i_partition_name IN VARCHAR2 DEFAULT NULL
    );

    PROCEDURE prc_nologging_table (
        i_owner      IN VARCHAR2
      , i_table_name IN VARCHAR2
    );

    PROCEDURE prc_logging_table (
        i_owner      IN VARCHAR2
      , i_table_name IN VARCHAR2
    );

    PROCEDURE prc_disable_constraint (
        i_owner      IN VARCHAR2
      , i_table_name IN VARCHAR2
    );

    PROCEDURE prc_enable_constraint (
        i_owner      IN VARCHAR2
      , i_table_name IN VARCHAR2
    );

    PROCEDURE prc_unusable_index (
        i_owner          IN VARCHAR2
      , i_table_name     IN VARCHAR2
      , i_partition_name IN VARCHAR2 DEFAULT NULL
    );

    PROCEDURE prc_rebuild_index (
        i_owner          IN VARCHAR2
      , i_table_name     IN VARCHAR2
      , i_partition_name IN VARCHAR2 DEFAULT NULL
    );

    PROCEDURE prc_truncate_table (
        i_owner      IN VARCHAR2
      , i_table_name IN VARCHAR2
    );

    PROCEDURE prc_truncate_partition (
        i_owner          IN VARCHAR2
      , i_table_name     IN VARCHAR2
      , i_partition_name IN VARCHAR2
    );

    PROCEDURE prc_truncate_subpartition (
        i_owner             IN VARCHAR2
      , i_table_name        IN VARCHAR2
      , i_subpartition_name IN VARCHAR2
    );

    PROCEDURE prc_gather_table_stats (
        i_owner          IN VARCHAR2
      , i_table_name     IN VARCHAR2
      , i_partition_name IN VARCHAR2 DEFAULT NULL
      , i_granularity    IN VARCHAR2 DEFAULT DBMS_STATS.DEFAULT_GRANULARITY
    );
END pkg_utl_etl;
/

CREATE OR REPLACE PACKAGE BODY pkg_utl_etl
IS
    --------------------------------------------------
    -- fnc_get_partition_name
    --------------------------------------------------
    FUNCTION fnc_get_partition_name (
        i_owner               IN VARCHAR2
      , i_table_name          IN VARCHAR2
      , i_partition_key_value IN VARCHAR2
    )
        RETURN VARCHAR2
    IS
        l_sql_text           VARCHAR2(32767);
        l_partition_position dba_tab_partitions.partition_position%TYPE;
        l_partition_name     dba_tab_partitions.partition_name%TYPE;
    BEGIN
        l_sql_text := 'SELECT TBL$OR$IDX$PART$NUM ({OWNER}.{TABLE_NAME}, 0, 1, 0, {PARTITION_KEY_VALUE}) FROM DUAL';
        l_sql_text := REPLACE (l_sql_text, '{OWNER}', i_owner);
        l_sql_text := REPLACE (l_sql_text, '{TABLE_NAME}', i_table_name);
        l_sql_text := REPLACE (l_sql_text, '{PARTITION_KEY_VALUE}', i_partition_key_value);

        EXECUTE IMMEDIATE l_sql_text INTO l_partition_position;

        SELECT partition_name
          INTO l_partition_name
          FROM dba_tab_partitions
         WHERE table_owner = i_owner
           AND table_name = i_table_name
           AND partition_position = l_partition_position;

        RETURN l_partition_name;
    EXCEPTION
        WHEN OTHERS THEN RETURN 'NONE';
    END fnc_get_partition_name;

    --------------------------------------------------
    -- fnc_get_subpartition_name
    --------------------------------------------------
    FUNCTION fnc_get_subpartition_name (
        i_owner                  IN VARCHAR2
      , i_table_name             IN VARCHAR2
      , i_subpartition_key_value IN VARCHAR2
    )
        RETURN VARCHAR2
    IS
        l_sql_text       VARCHAR2(32767);
        l_object_id      dba_objects.object_id%TYPE;
        l_subobject_name dba_objects.subobject_name%TYPE;
    BEGIN
        l_sql_text := 'SELECT TBL$OR$IDX$PART$NUM ({OWNER}.{TABLE_NAME}, 0, 3, 0, {SUBPARTITION_KEY_VALUE}) FROM DUAL';
        l_sql_text := REPLACE (l_sql_text, '{OWNER}', i_owner);
        l_sql_text := REPLACE (l_sql_text, '{TABLE_NAME}', i_table_name);
        l_sql_text := REPLACE (l_sql_text, '{SUBPARTITION_KEY_VALUE}', i_subpartition_key_value);

        EXECUTE IMMEDIATE l_sql_text INTO l_object_id;

        SELECT subobject_name
          INTO l_subobject_name
          FROM dba_objects
         WHERE object_id = l_object_id;

        RETURN l_subobject_name;
    EXCEPTION
        WHEN OTHERS THEN RETURN 'NONE';
    END fnc_get_subpartition_name;

    --------------------------------------------------
    -- fnc_get_degree
    --------------------------------------------------
    FUNCTION fnc_get_degree (
        i_owner          IN VARCHAR2
      , i_segment_name   IN VARCHAR2
      , i_partition_name IN VARCHAR2 DEFAULT NULL
    )
        RETURN NUMBER
    IS
        l_bytes dba_segments.bytes%TYPE;
    BEGIN
        IF i_partition_name IS NULL
        THEN
            SELECT SUM (bytes)
              INTO l_bytes
              FROM dba_segments
             WHERE owner = i_owner
               AND segment_name = i_segment_name;
        ELSE
            SELECT bytes
              INTO l_bytes
              FROM dba_segments
             WHERE owner = i_owner
               AND segment_name = i_segment_name
               AND partition_name = i_partition_name;
        END IF;

        RETURN GREATEST (LEAST (CEIL (l_bytes / POWER (1024, 3)), 32), 4); -- !
    EXCEPTION
        WHEN OTHERS THEN RETURN 2;
    END fnc_get_degree;

    --------------------------------------------------
    -- prc_begin_etl
    --------------------------------------------------
    PROCEDURE prc_begin_etl (
        i_owner          IN VARCHAR2
      , i_table_name     IN VARCHAR2
      , i_partition_name IN VARCHAR2 DEFAULT NULL
    )
    IS
    BEGIN
        prc_nologging_table    (i_owner, i_table_name);
        prc_disable_constraint (i_owner, i_table_name);
        prc_unusable_index     (i_owner, i_table_name, i_partition_name);
    END prc_begin_etl;

    --------------------------------------------------
    -- prc_end_etl
    --------------------------------------------------
    PROCEDURE prc_end_etl (
        i_owner          IN VARCHAR2
      , i_table_name     IN VARCHAR2
      , i_partition_name IN VARCHAR2 DEFAULT NULL
    )
    IS
    BEGIN
        prc_rebuild_index     (i_owner, i_table_name, i_partition_name);
        prc_enable_constraint (i_owner, i_table_name);
        prc_logging_table     (i_owner, i_table_name);
    END prc_end_etl;

    --------------------------------------------------
    -- prc_nologging_table
    --------------------------------------------------
    PROCEDURE prc_nologging_table (
        i_owner      IN VARCHAR2
      , i_table_name IN VARCHAR2
    )
    IS
    BEGIN
        EXECUTE IMMEDIATE 'ALTER TABLE ' || i_owner || '.' || i_table_name || ' NOLOGGING';
    END prc_nologging_table;

    --------------------------------------------------
    -- prc_logging_table
    --------------------------------------------------
    PROCEDURE prc_logging_table (
        i_owner      IN VARCHAR2
      , i_table_name IN VARCHAR2
    )
    IS
    BEGIN
        EXECUTE IMMEDIATE 'ALTER TABLE ' || i_owner || '.' || i_table_name || ' LOGGING';
    END prc_logging_table;

    --------------------------------------------------
    -- prc_disable_constraint
    --------------------------------------------------
    PROCEDURE prc_disable_constraint (
        i_owner      IN VARCHAR2
      , i_table_name IN VARCHAR2
    )
    IS
    BEGIN
        FOR f IN (SELECT constraint_name
                    FROM dba_constraints
                   WHERE owner = i_owner
                     AND table_name = i_table_name
                     AND constraint_type IN ('P', 'U')
                     AND status = 'ENABLED')
        LOOP
            EXECUTE IMMEDIATE 'ALTER TABLE ' || i_owner || '.' || i_table_name || ' MODIFY CONSTRAINT ' || f.constraint_name || ' DISABLE KEEP INDEX';
        END LOOP;
    END prc_disable_constraint;

    --------------------------------------------------
    -- prc_enable_constraint
    --------------------------------------------------
    PROCEDURE prc_enable_constraint (
        i_owner      IN VARCHAR2
      , i_table_name IN VARCHAR2
    )
    IS
    BEGIN
        FOR f IN (SELECT constraint_name
                    FROM dba_constraints
                   WHERE owner = i_owner
                     AND table_name = i_table_name
                     AND constraint_type IN ('P', 'U')
                     AND status = 'DISABLED')
        LOOP
            EXECUTE IMMEDIATE 'ALTER TABLE ' || i_owner || '.' || i_table_name || ' MODIFY CONSTRAINT ' || f.constraint_name || ' ENABLE';
        END LOOP;
    END prc_enable_constraint;

    --------------------------------------------------
    -- prc_unusable_index
    --------------------------------------------------
    PROCEDURE prc_unusable_index (
        i_owner          IN VARCHAR2
      , i_table_name     IN VARCHAR2
      , i_partition_name IN VARCHAR2 DEFAULT NULL
    )
    IS
    BEGIN
        FOR f IN (SELECT owner
                       , index_name
                    FROM dba_indexes
                   WHERE table_owner = i_owner
                     AND table_name = i_table_name
                     AND status = 'VALID')
        LOOP
            EXECUTE IMMEDIATE 'ALTER INDEX ' || f.owner || '.' || f.index_name || ' UNUSABLE';
        END LOOP;

        FOR f IN (SELECT a.owner
                       , a.index_name
                       , b.partition_name
                    FROM dba_part_indexes a
                       , dba_ind_partitions b
                   WHERE a.owner = i_owner
                     AND a.table_name = i_table_name
                     AND a.subpartitioning_type = 'NONE'
                     AND a.locality = 'LOCAL'
                     AND b.index_owner = a.owner
                     AND b.index_name = a.index_name
                     AND b.partition_name = NVL (i_partition_name, partition_name)
                     AND b.status = 'USABLE')
        LOOP
            EXECUTE IMMEDIATE 'ALTER INDEX ' || f.owner || '.' || f.index_name || ' MODIFY PARTITION ' || f.partition_name || ' UNUSABLE';
        END LOOP;

        FOR f IN (SELECT a.owner
                       , a.index_name
                       , b.subpartition_name
                    FROM dba_part_indexes a
                       , dba_ind_subpartitions b
                   WHERE a.owner = i_owner
                     AND a.table_name = i_table_name
                     AND a.subpartitioning_type <> 'NONE'
                     AND a.locality = 'LOCAL'
                     AND b.index_owner = a.owner
                     AND b.index_name = a.index_name
                     AND b.subpartition_name = NVL (i_partition_name, subpartition_name)
                     AND b.status = 'USABLE')
        LOOP
            EXECUTE IMMEDIATE 'ALTER INDEX ' || f.owner || '.' || f.index_name || ' MODIFY SUBPARTITION ' || f.subpartition_name || ' UNUSABLE';
        END LOOP;
    END prc_unusable_index;

    --------------------------------------------------
    -- prc_rebuild_index
    --------------------------------------------------
    PROCEDURE prc_rebuild_index (
        i_owner          IN VARCHAR2
      , i_table_name     IN VARCHAR2
      , i_partition_name IN VARCHAR2 DEFAULT NULL
    )
    IS
        l_degree NUMBER;
    BEGIN
        l_degree := fnc_get_degree (i_owner, i_table_name);

        FOR f IN (SELECT owner
                       , index_name
                    FROM dba_indexes
                   WHERE table_owner = i_owner
                     AND table_name = i_table_name
                     AND status = 'UNUSABLE')
        LOOP
            EXECUTE IMMEDIATE 'ALTER INDEX ' || f.owner || '.' || f.index_name || ' REBUILD NOLOGGING PARALLEL ' || l_degree;
            EXECUTE IMMEDIATE 'ALTER INDEX ' || f.owner || '.' || f.index_name || ' LOGGING NOPARALLEL';
        END LOOP;

        FOR f IN (SELECT a.owner
                       , a.index_name
                       , b.partition_name
                    FROM dba_part_indexes a
                       , dba_ind_partitions b
                   WHERE a.owner = i_owner
                     AND a.table_name = i_table_name
                     AND a.subpartitioning_type = 'NONE'
                     AND a.locality = 'LOCAL'
                     AND b.index_owner = a.owner
                     AND b.index_name = a.index_name
                     AND b.partition_name = NVL (i_partition_name, partition_name)
                     AND b.status = 'UNUSABLE')
        LOOP
            l_degree := fnc_get_degree (i_owner, i_table_name, f.partition_name);
            EXECUTE IMMEDIATE 'ALTER INDEX ' || f.owner || '.' || f.index_name || ' REBUILD PARTITION ' || f.partition_name || ' NOLOGGING PARALLEL ' || l_degree;
            EXECUTE IMMEDIATE 'ALTER INDEX ' || f.owner || '.' || f.index_name || ' MODIFY  PARTITION ' || f.partition_name || ' LOGGING';
        END LOOP;

        FOR f IN (SELECT a.owner
                       , a.index_name
                       , b.subpartition_name
                    FROM dba_part_indexes a
                       , dba_ind_subpartitions b
                   WHERE a.owner = i_owner
                     AND a.table_name = i_table_name
                     AND a.subpartitioning_type <> 'NONE'
                     AND a.locality = 'LOCAL'
                     AND b.index_owner = a.owner
                     AND b.index_name = a.index_name
                     AND b.subpartition_name = NVL (i_partition_name, subpartition_name)
                     AND b.status = 'UNUSABLE')
        LOOP
            l_degree := fnc_get_degree (i_owner, i_table_name, f.subpartition_name);
            EXECUTE IMMEDIATE 'ALTER INDEX ' || f.owner || '.' || f.index_name || ' REBUILD SUBPARTITION ' || f.subpartition_name || ' NOLOGGING PARALLEL ' || l_degree;
            EXECUTE IMMEDIATE 'ALTER INDEX ' || f.owner || '.' || f.index_name || ' MODIFY  SUBPARTITION ' || f.subpartition_name || ' LOGGING';
        END LOOP;
    END prc_rebuild_index;

    --------------------------------------------------
    -- prc_truncate_table
    --------------------------------------------------
    PROCEDURE prc_truncate_table (
        i_owner             IN VARCHAR2
      , i_table_name        IN VARCHAR2
    )
    IS
    BEGIN
        EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || i_owner || '.' || i_table_name || ' DROP ALL STORAGE';
    END prc_truncate_table;

    --------------------------------------------------
    -- prc_truncate_partition
    --------------------------------------------------
    PROCEDURE prc_truncate_partition (
        i_owner          IN VARCHAR2
      , i_table_name     IN VARCHAR2
      , i_partition_name IN VARCHAR2
    )
    IS
    BEGIN
        EXECUTE IMMEDIATE 'ALTER TABLE ' || i_owner || '.' || i_table_name || ' TRUNCATE PARTITION ' || i_partition_name || ' DROP ALL STORAGE';
    END prc_truncate_partition;

    --------------------------------------------------
    -- prc_truncate_subpartition
    --------------------------------------------------
    PROCEDURE prc_truncate_subpartition (
        i_owner             IN VARCHAR2
      , i_table_name        IN VARCHAR2
      , i_subpartition_name IN VARCHAR2
    )
    IS
    BEGIN
        EXECUTE IMMEDIATE 'ALTER TABLE ' || i_owner || '.' || i_table_name || ' TRUNCATE SUBPARTITION ' || i_subpartition_name || ' DROP ALL STORAGE';
    END prc_truncate_subpartition;

    --------------------------------------------------
    -- prc_gather_table_stats
    --------------------------------------------------
    PROCEDURE prc_gather_table_stats (
        i_owner          IN VARCHAR2
      , i_table_name     IN VARCHAR2
      , i_partition_name IN VARCHAR2 DEFAULT NULL
      , i_granularity    IN VARCHAR2 DEFAULT DBMS_STATS.DEFAULT_GRANULARITY
    )
    IS
        l_degree NUMBER;
    BEGIN
        l_degree := fnc_get_degree (i_owner, i_table_name, i_partition_name);

        CASE
            WHEN i_partition_name IS NULL
            THEN DBMS_STATS.GATHER_TABLE_STATS (ownname => i_owner, tabname => i_table_name, degree => l_degree);
            ELSE DBMS_STATS.GATHER_TABLE_STATS (ownname => i_owner, tabname => i_table_name, partname => i_partition_name, degree => l_degree, granularity => i_granularity);
        END CASE;
    END prc_gather_table_stats;
END pkg_utl_etl;
/


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

다형성 테이블 함수  (0) 2019.01.04
단위 변환 함수  (0) 2018.12.31
에러 메시지 조회 함수  (0) 2018.12.24
런타임 실행 계획 조회 함수  (0) 2018.12.12
RAW 변환 함수  (0) 2018.11.13
Posted by 정희락_