ETL通用解決方案---oracle+儲存過程 實現

abraham_dba_2013發表於2015-01-12

方案理念--四化理念


  1. 資料規格化
  2. 處理自動化
  3. 資訊集中化
  4. 操作人性化


架構

通過檢視實現來至兩個不同資料庫的表的結構完全一致,  在結構完全相同的兩個表之間進行資料同步, 問題變得相當簡單. 同步程式碼如下.

 

 

ETL


---初始同步

delete from ods_table;

insert into v_table

select * from db_table;

commit;

 

---新增同步

insert into v_table

select * from db_table t

where t.id not in (select id from v_table);

 

commit;

 

 

---變更同步

 

update ods_table t

set t.c_number = (select db.c_number from db_table db where db.id = t.id)

where t.c_number != (select db.c_number from db_table db where db.id = t.id);

 

commit;


實現兩個表結構完全一致的方法如下

---建表

CREATE SEQUENCE SEQ_ETL_INCREASE_ID

INCREMENT BY 1

START WITH 1

  NOCACHE;

 

/*==============================================================*/

/* Table: ETL_TABLES                                            */

/*==============================================================*/

CREATE TABLE ETL_TABLES  (

   "ID"                 NUMBER                         DEFAULT -1 NOT NULL,

   "TABLE_NAME"         VARCHAR2(100)                   NOT NULL,

   "TABLE_TYPE"         VARCHAR2(30)                    NOT NULL,

   "TABLE_ROOT_IN"      VARCHAR2(30),

   "TABLE_NEED_CREATE_VIEW" NUMBER                         DEFAULT 1,

   "TABLE_CREATE_VIEW_NAME_PREFIX" VARCHAR2(30)                   DEFAULT 'v',

   "DB_LINK_NAME"       VARCHAR2(100),

   "CURRENT_VERSION"    NUMBER                         DEFAULT 1 NOT NULL,

   "VERSION_HISTORY"    VARCHAR2(3000)                 DEFAULT 'init input' NOT NULL,

   "DEVELOP_DATE"       DATE                           DEFAULT SYSDATE NOT NULL,

   "DEVELOP_BY"         VARCHAR2(100)                  DEFAULT 'cyyan@isoftstone' NOT NULL,

   "LAST_MAINTAIN_DATE" DATE                           DEFAULT SYSDATE NOT NULL,

   "LAST_MAINTAIN_BY"   VARCHAR2(100)                  DEFAULT 'cyyan@isoftstone' NOT NULL,

   "MEMO"               VARCHAR2(500),

   "STATUS"             NUMBER                         DEFAULT 1,

   CONSTRAINT PK_ETL_TABLES PRIMARY KEY ("ID")

);

 

COMMENT ON TABLE ETL_TABLES IS

'此表用於維護ETL涉及到所有表, 包括:

1, db---業務系統資料庫

2, ods---運算元據資料庫

3, dw---資料倉儲';

 

/*==============================================================*/

/* Table: ETL_VIEWS                                             */

/*==============================================================*/

CREATE TABLE ETL_VIEWS  (

   "ID"                 NUMBER                         DEFAULT -1 NOT NULL,

   "VIEW_NAME"          VARCHAR2(100)                   NOT NULL,

   "VIEW_TYPE"          VARCHAR2(30)                    NOT NULL,

   "VIEW_ROOT_IN"       VARCHAR2(30),

   "VIEW_SELECT"        VARCHAR2(4000)                  NOT NULL,

   "VIEW_FROM"          VARCHAR2(600)                   NOT NULL,

   "VIEW_WHERE"         VARCHAR2(2000),

   "VIEW_ORDER_BY"      VARCHAR2(600),

   "VIEW_GROUP_BY"      VARCHAR2(600),

   "VIEW_HAVING"        VARCHAR2(600),

   "VIEW_DB_LINK_NAME"  VARCHAR2(100),

   "CURRENT_VERSION"    NUMBER                         DEFAULT 1 NOT NULL,

   "VERSION_HISTORY"    VARCHAR2(3000)                 DEFAULT 'init input' NOT NULL,

   "DEVELOP_DATE"       DATE                           DEFAULT SYSDATE NOT NULL,

   "DEVELOP_BY"         VARCHAR2(100)                  DEFAULT 'cyyan@isoftstone' NOT NULL,

   "LAST_MAINTAIN_DATE" DATE                           DEFAULT SYSDATE NOT NULL,

   "LAST_MAINTAIN_BY"   VARCHAR2(100)                  DEFAULT 'cyyan@isoftstone' NOT NULL,

   "MEMO"               VARCHAR2(500),

   "STATUS"             NUMBER                         DEFAULT 1,

   CONSTRAINT PK_ETL_VIEWS PRIMARY KEY ("ID")

);

 

COMMENT ON TABLE ETL_VIEWS IS

'此表用於維護ETL涉及到所有檢視, 包括:

1, v1---db表中與ods對應到檢視

2, v2---ods表中與db對應到檢視

3, v3---ods表中與dw對應到檢視

4, v4---dw表中與ods中對應到檢視';

 

/*==============================================================*/

/* Table: ETLS                                                  */

/*==============================================================*/

CREATE TABLE ETLS  (

   "ID"                 NUMBER                          NOT NULL,

   "ETL_NAME"           VARCHAR2(300)                   NOT NULL,

   "ETL_TYPE"           VARCHAR2(30)                    NOT NULL,

   "ETL_SRC_VIEW_OR_TABLE" NUMBER                          NOT NULL,

   "ETL_DES_VIEW_OR_TABLE" NUMBER                          NOT NULL,

   "ETL_INIT_ENABLE"    NUMBER(1)                      DEFAULT 1 NOT NULL,

   "ETL_ADD_ENABLE"     NUMBER(1)                      DEFAULT 1 NOT NULL,

   "ETL_CHARGE_ENABLE"  NUMBER(1)                      DEFAULT 1 NOT NULL,

   "CURRENT_VERSION"    NUMBER                         DEFAULT 1 NOT NULL,

   "VERSION_HISTORY"    VARCHAR2(3000)                 DEFAULT 'init input' NOT NULL,

   "DEVELOP_DATE"       DATE                           DEFAULT SYSDATE NOT NULL,

   "DEVELOP_BY"         VARCHAR2(100)                  DEFAULT 'cyyan@isoftstone' NOT NULL,

   "LAST_MAINTAIN_DATE" DATE                           DEFAULT SYSDATE NOT NULL,

   "LAST_MAINTAIN_BY"   VARCHAR2(100)                  DEFAULT 'cyyan@isoftstone' NOT NULL,

   "MEMO"               VARCHAR2(500),

   "STATUS"             NUMBER                         DEFAULT 1,

   CONSTRAINT PK_ETLS PRIMARY KEY ("ID")

);

 

COMMENT ON TABLE ETLS IS

'此表用於維護ETL轉換時設計到源表和目的表

 

源表(或檢視)---&gt目的表(或檢視)-

(推薦全部使用檢視, 檢視具有更過到靈活性, 而且更統一)

 

整體架構是在完全相同兩張表(或檢視)之間進行同步處理

 

規範:

1,  源表(或檢視)-和目的表(或檢視)-完全相同

2,  目的檢視必須是單表';



--儲存過程


/*==============================================================*/
/* Database name:  %DATABASE%                                   */
/* DBMS name:      ORACLE Version 10g                           */
/* Created on:     2009-2-1 23:29:27                            */
/*==============================================================*/



-- INTEGRITY PACKAGE DECLARATION
CREATE OR REPLACE PACKAGE INTEGRITYPACKAGE AS
 PROCEDURE INITNESTLEVEL;
 FUNCTION GETNESTLEVEL RETURN NUMBER;
 PROCEDURE NEXTNESTLEVEL;
 PROCEDURE PREVIOUSNESTLEVEL;
 END INTEGRITYPACKAGE;
/

-- INTEGRITY PACKAGE DEFINITION
CREATE OR REPLACE PACKAGE BODY INTEGRITYPACKAGE AS
 NESTLEVEL NUMBER;

-- PROCEDURE TO INITIALIZE THE TRIGGER NEST LEVEL
 PROCEDURE INITNESTLEVEL IS
 BEGIN
 NESTLEVEL := 0;
 END;


-- FUNCTION TO RETURN THE TRIGGER NEST LEVEL
 FUNCTION GETNESTLEVEL RETURN NUMBER IS
 BEGIN
 IF NESTLEVEL IS NULL THEN
     NESTLEVEL := 0;
 END IF;
 RETURN(NESTLEVEL);
 END;

-- PROCEDURE TO INCREASE THE TRIGGER NEST LEVEL
 PROCEDURE NEXTNESTLEVEL IS
 BEGIN
 IF NESTLEVEL IS NULL THEN
     NESTLEVEL := 0;
 END IF;
 NESTLEVEL := NESTLEVEL + 1;
 END;

-- PROCEDURE TO DECREASE THE TRIGGER NEST LEVEL
 PROCEDURE PREVIOUSNESTLEVEL IS
 BEGIN
 NESTLEVEL := NESTLEVEL - 1;
 END;

 END INTEGRITYPACKAGE;
/




CREATE OR REPLACE PROCEDURE PRO_CREATE_VIEW_BY_ETL_VIEWS
AS

--------------PRO_CREATE_VIEW_BY_ETL_VIEWS------------------------
-- CREATED ON 2009-2-1 BY CYYAN@ISOFTSTONE
-- 功能 : 根據ETL_VIEWS中到資料生成檢視
------------------------------------------------------------------------------


  VIEW_CREATE_CODE VARCHAR2(10000); --生成檢視到程式碼

  VIEW_NAME VARCHAR2(100);  --檢視名稱

  VIEW_SELECT VARCHAR2(4000);  --檢視的SELECT部分
  VIEW_FROM VARCHAR2(300);   --檢視的FROM部分
  VIEW_WHERE VARCHAR2(3000);   --檢視的WHERE部分
  VIEW_ORDER_BY VARCHAR2(600);   --檢視的ORDER BY部分
  VIEW_GROUP_BY VARCHAR2(600);   --檢視的GROUP BY部分
  VIEW_HAVING VARCHAR2(600);   --檢視的HAVING部分
  VIEW_DB_LINK_NAME VARCHAR2(100);   --檢視的DB LINK部分

  ROW_COUNT NUMBER; --行數

  CURSOR ETL_VIEWS_CURSOR IS  --提取建立檢視需要到資訊
    SELECT VIEW_NAME, VIEW_SELECT, VIEW_FROM, VIEW_WHERE, VIEW_ORDER_BY, VIEW_GROUP_BY, VIEW_HAVING, VIEW_DB_LINK_NAME FROM ETL_VIEWS T WHERE T.CURRENT_VERSION = (SELECT MAX(T2.CURRENT_VERSION) FROM ETL_VIEWS T2 WHERE T.VIEW_NAME = T2.VIEW_NAME);

BEGIN
  -- 統計行數
  SELECT COUNT(*) INTO ROW_COUNT  FROM ETL_VIEWS T WHERE T.CURRENT_VERSION = (SELECT MAX(T2.CURRENT_VERSION) FROM ETL_VIEWS T2 WHERE T.VIEW_NAME = T2.VIEW_NAME);

  OPEN ETL_VIEWS_CURSOR; --開啟遊標
  FOR I IN 1 .. ROW_COUNT  LOOP  --遍歷
    FETCH ETL_VIEWS_CURSOR
      INTO VIEW_NAME, VIEW_SELECT, VIEW_FROM, VIEW_WHERE, VIEW_ORDER_BY, VIEW_GROUP_BY, VIEW_HAVING, VIEW_DB_LINK_NAME;

      ---拼接建立檢視到語句
    VIEW_CREATE_CODE := 'create or replace view ' || VIEW_NAME || ' as select ' || VIEW_SELECT || ' from ' || VIEW_FROM;


    IF VIEW_DB_LINK_NAME IS NOT NULL THEN
       VIEW_CREATE_CODE := VIEW_CREATE_CODE || '@' || VIEW_DB_LINK_NAME;
    END IF;

    IF VIEW_WHERE IS NOT NULL THEN
       VIEW_CREATE_CODE := VIEW_CREATE_CODE || ' where ' || VIEW_WHERE;
    END IF;

    IF VIEW_ORDER_BY IS NOT NULL THEN
       VIEW_CREATE_CODE := VIEW_CREATE_CODE || ' order by ' || VIEW_ORDER_BY;
    END IF;

    IF VIEW_GROUP_BY IS NOT NULL THEN
       VIEW_CREATE_CODE := VIEW_CREATE_CODE || ' group by ' || VIEW_GROUP_BY;
    END IF;

    IF VIEW_HAVING IS NOT NULL THEN
       VIEW_CREATE_CODE := VIEW_CREATE_CODE || ' having ' || VIEW_HAVING;
    END IF;

    --輸出建立語句
    --DBMS_OUTPUT.PUT_LINE(VIEW_CREATE_CODE);
    --DBMS_OUTPUT.PUT_LINE('');
    --執行建立檢視
    EXECUTE IMMEDIATE VIEW_CREATE_CODE;
  END LOOP;
  CLOSE ETL_VIEWS_CURSOR; --關閉遊標
END;
/


CREATE OR REPLACE PROCEDURE PRO_INSERT_INTO_ETL_VIEWS
AS

--ADD BY CYYAN@ISOFTSTONE
--2009年2月1日21:33:37
---此儲存過程用於 將ETL_TABLE中標識需要建立VIEW 到TABLE, 進行自動提起轉換到ETL_VIEWS中.
--處理過程用到啦系統表COL從此表中獲取列名

  TABLE_NAME VARCHAR2(100); --表名
  COL_NAME   VARCHAR2(100); --列名

  TABLE_COUNT NUMBER; --表到行數
  --COL_COUNT   NUMBER; --列數

  ETL_VIEWS_INSERT_CODE VARCHAR2(600); --插入語句到 INSERT部分
  ETL_VIEWS_VALUES_CODE VARCHAR2(16000); --插入語句到VALUES部分

  --ETL_VIEWS的到列
  VIEW_NAME_PREFIX VARCHAR2(30);--實體名到字首
  TABLE_TYPE VARCHAR2(30); --表型別 如 DB, ODS, DW
  TABLE_ROOT_IN VARCHAR2(30); --表來源, 來自那個系統, 如資金系統"NHZJ", 財務系統"NHCW"
  VIEW_SELECT VARCHAR2(10000); --VIEW 語句到SELECT部分, 這個需要遍歷一個表到所有列
  DB_LINK_NAME VARCHAR2(100);
  CURRENT_VERSION VARCHAR2(600); --版本部分, 這裡沒更新, 只要全部刪除, 或不斷插入, 此欄位定義了版本, 沒有變更都形成新到版本, 取值是取最大值

  CURSOR_NUMBER NUMBER;
   COL_SELECT_SQL VARCHAR2(100);
    RETURN_VALUE NUMBER;
  --從ETL_TABLES中查詢需要生成檢視到表
  CURSOR DB_TABLES_CURSOR IS
    SELECT UPPER(TABLE_NAME), T.TABLE_TYPE, T.TABLE_ROOT_IN, T.TABLE_CREATE_VIEW_NAME_PREFIX, DB_LINK_NAME FROM ETL_TABLES T WHERE (UPPER(T.TABLE_TYPE) = 'DB' OR UPPER(T.TABLE_TYPE) = 'DW' ) AND T.TABLE_NEED_CREATE_VIEW = 1;

  --CURSOR_NUMBER NUMBER; --遊標 OLD WAY 執行需要, NEW WAY 不需要
  --RETURN_VALUE NUMBER; --執行後返回值  OLD WAY 執行需要, NEW WAY 不需要


BEGIN
  -- TEST STATEMENTS HERE
  SELECT COUNT(*) INTO TABLE_COUNT FROM ETL_TABLES T WHERE (UPPER(T.TABLE_TYPE) = 'DB' OR UPPER(T.TABLE_TYPE) = 'DW' ) AND T.TABLE_NEED_CREATE_VIEW = 1;

  --構造INSERT部分
  ETL_VIEWS_INSERT_CODE  := 'insert into etl_views(view_name, view_type, view_root_in, view_select, view_from, current_version, VIEW_DB_LINK_NAME) ';

  OPEN DB_TABLES_CURSOR;
  FOR I IN 1 .. TABLE_COUNT LOOP --表遍歷
    FETCH DB_TABLES_CURSOR
      INTO TABLE_NAME, TABLE_TYPE, TABLE_ROOT_IN, VIEW_NAME_PREFIX, DB_LINK_NAME;
      --構造VALUES部分
      ETL_VIEWS_VALUES_CODE  := 'values(''' || VIEW_NAME_PREFIX || TABLE_NAME || ''', ''' ||  TABLE_TYPE || ''', ''' || TABLE_ROOT_IN || '''';

    DBMS_OUTPUT.PUT(TABLE_NAME);

/*   使用CURSOR遍歷列到方法 不適用於DB_LINK
     --準備遍歷列
    SELECT COUNT(*) INTO COL_COUNT FROM COL@DB_LINK_NHZJ WHERE COL.TNAME = UPPER(TABLE_NAME);
    DBMS_OUTPUT.PUT_LINE(' table has ' || COL_COUNT || ' cols');
    DECLARE
      CURSOR COLS_CURSOR IS
        SELECT C.CNAME FROM COL@DB_LINK_NHZJ C WHERE C.TNAME = UPPER(TABLE_NAME);

    BEGIN
      OPEN COLS_CURSOR;
      VIEW_SELECT := '';

      --下面用逗號拼接列
      FETCH COLS_CURSOR --遍歷第一列
          INTO COL_NAME;

      VIEW_SELECT := VIEW_SELECT || COL_NAME;

      FOR J IN 2 .. COL_COUNT LOOP --遍歷後面到列
        FETCH COLS_CURSOR
          INTO COL_NAME;
        DBMS_OUTPUT.PUT_LINE('  ' || COL_NAME);
        VIEW_SELECT := VIEW_SELECT || ', ' || COL_NAME;
      END LOOP;
      CLOSE COLS_CURSOR;
    END;

    --DBMS_OUTPUT.PUT_LINE(VIEW_SELECT);
   -- DBMS_OUTPUT.PUT_LINE(ETL_VIEWS_VALUES_CODE);
  */

    /* 使用 DBMS_SQL */
    -- ANOTHER WAY USER DBMS_SQL PACKAGE
    COL_SELECT_SQL := 'select t.cname from sys.col@' || DB_LINK_NAME || ' T where T.tname = ''' || TABLE_NAME || '''';
    --SQL_CODE := 'select t.cname from sys.col T where T.tname = ''' || TABLE_NAME || '''';

    CURSOR_NUMBER := DBMS_SQL.OPEN_CURSOR();

   DBMS_SQL.PARSE(CURSOR_NUMBER, COL_SELECT_SQL, DBMS_SQL.NATIVE);

    DBMS_SQL.DEFINE_COLUMN(CURSOR_NUMBER,1,COL_NAME, 100);


   RETURN_VALUE := DBMS_SQL.EXECUTE(CURSOR_NUMBER);
    DBMS_OUTPUT.PUT_LINE('  RETURN_VALUE = ' || RETURN_VALUE);

    RETURN_VALUE := DBMS_SQL.FETCH_ROWS(CURSOR_NUMBER); --獲取第一列
   DBMS_SQL.COLUMN_VALUE(CURSOR_NUMBER,1,COL_NAME);
     VIEW_SELECT := COL_NAME;

   WHILE DBMS_SQL.FETCH_ROWS(CURSOR_NUMBER)<>0 LOOP  ---遍歷其它到列

    DBMS_SQL.COLUMN_VALUE(CURSOR_NUMBER,1,COL_NAME);
    DBMS_OUTPUT.PUT_LINE(COL_NAME);
     VIEW_SELECT := VIEW_SELECT || ', ' || COL_NAME;
   END LOOP;

--   DBMS_OUTPUT.PUT_LINE('VIEW_SELECT : ' || VIEW_SELECT);

   DBMS_SQL.CLOSE_CURSOR(CURSOR_NUMBER);

    --生成最新到版本號: 檢視名稱是唯一的
    SELECT NVL(MAX(CURRENT_VERSION),0) + 1 INTO CURRENT_VERSION FROM ETL_VIEWS V WHERE V.VIEW_NAME = VIEW_NAME_PREFIX || TABLE_NAME;
    ETL_VIEWS_VALUES_CODE  := ETL_VIEWS_VALUES_CODE || CHR(10) || ', ''' || VIEW_SELECT || '''' || CHR(10) || ', ''' || TABLE_NAME || ''', ''' || CURRENT_VERSION || ''', ''' || DB_LINK_NAME || ''')';

    --輸出插入到語句
    --DBMS_OUTPUT.PUT_LINE(ETL_VIEWS_INSERT_CODE);
     --DBMS_OUTPUT.PUT_LINE(ETL_VIEWS_VALUES_CODE);
    --DBMS_OUTPUT.PUT_LINE('');

--DBMS_STANDARD.

    --執行插入語句
    -- NEW WAY
    EXECUTE IMMEDIATE ETL_VIEWS_INSERT_CODE || ETL_VIEWS_VALUES_CODE;
/*
    -- OLD WAY
   CURSOR_NUMBER := DBMS_SQL.OPEN_CURSOR();
   DBMS_SQL.PARSE(CURSOR_NUMBER, ETL_VIEWS_INSERT_CODE, DBMS_SQL.NATIVE);
   RETURN_VALUE := DBMS_SQL.EXECUTE(CURSOR_NUMBER);
   DBMS_SQL.CLOSE_CURSOR(CURSOR_NUMBER);
  */
  END LOOP;
  COMMIT; --提交
  CLOSE DB_TABLES_CURSOR; --關閉遊標
  --EXCEPTION
        --ROLLBACK;
END;
/


CREATE OR REPLACE TRIGGER TRG_ID_ON_ETLS
  BEFORE INSERT ON ETLS  
  FOR EACH ROW
DECLARE
  -- NOTHING
BEGIN
  SELECT SEQ_ETL_INCREASE_ID.NEXTVAL INTO :NEW.ID FROM DUAL;
END TRIGGER_ID_INCREASE;
/


CREATE OR REPLACE TRIGGER TRG_ID_ON_ETL_TABLES
  BEFORE INSERT ON ETL_TABLES  
  FOR EACH ROW
DECLARE
  -- NOTHING
BEGIN
  SELECT SEQ_ETL_INCREASE_ID.NEXTVAL INTO :NEW.ID FROM DUAL;
END TRIGGER_ID_INCREASE;
/


CREATE OR REPLACE TRIGGER TRG_ID_ON_ETL_VIEWS
  BEFORE INSERT ON ETL_VIEWS
  FOR EACH ROW
DECLARE
  -- NOTHING
BEGIN
  SELECT SEQ_ETL_INCREASE_ID.NEXTVAL INTO :NEW.ID FROM DUAL;
END TRIGGER_ID_INCREASE;
/
引用:http://pipe-line.iteye.com/blog/321719

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29439655/viewspace-1399266/,如需轉載,請註明出處,否則將追究法律責任。

相關文章