[Oracle]重置序號 / 取號依日期格式化

[Oracle]重置序號 / 取號依日期格式化

1) 以Package + StoredProcedure + Function 實作

2) 重置序號實作方式分為 A. 原物件倒序 B. DROP/CREATE 重新建立

3) 取號時則以日期格式(YYYYMMDD) + 數字格式(ssss) 回傳

 

記載系統用的序號物件名稱與表單的關聯

CREATE TABLE MY_DOCUMENT (
  DOC_TYPE VARCHAR2(128),
  NUM_SEQ_NAME VARCHAR2(100)
)

 

/*************************************************************************
PART-1
NAME:     MY_SEQ
PURPOSE:  處理序號物件 PACKAGE
*************************************************************************/
create or replace
PACKAGE        MY_SEQ
IS
   /*************************************************************************
    NAME:       GET_NUM_SEQ_NAME
    PURPOSE:    回傳序號物件名稱 by DOC_TYPE (See MY_DOCUMENT)
    RETURN:     序號物件名稱
    EXCEPTION:  If DOC_TYPE  not in MY_DOCUMENT.
    *************************************************************************/
   FUNCTION GET_NUM_SEQ_NAME (pDOC_TYPE IN VARCHAR2)
      RETURN VARCHAR2;
 
   /*************************************************************************
    NAME:       NEXT_SEQ_BY_TYPE
    PURPOSE:    回傳序號 by DOC_TYPE (See MY_DOCUMENT)
    RETURN:     Default is (YYYYMMDDssss). If s>9999回傳(YYYYMMDDsssss)s不格式化.
    EXCEPTION:  If DOC_TYPE  not in MY_DOCUMENT.
    *************************************************************************/
   FUNCTION NEXT_SEQ_BY_TYPE (pDOC_TYPE IN VARCHAR2)
      RETURN VARCHAR2;
 
   /*************************************************************************
    NAME:       NEXT_SEQ
    PURPOSE:    回傳序號
    RETURN:     Default is (YYYYMMDDssss). If s>9999回傳(YYYYMMDDsssss)s不格式化.
    *************************************************************************/
   FUNCTION NEXT_SEQ (pNUM_SEQ_NAME IN VARCHAR2)
      RETURN VARCHAR2;
 
   /*************************************************************************
   NAME:       RESET_SEQ_BY_TYPE
   PURPOSE:    重置序號物件 by DOC_TYPE (See MY_DOCUMENT)
   EXCEPTION:  If DOC_TYPE  not in MY_DOCUMENT.
   *************************************************************************/
   PROCEDURE RESET_SEQ_BY_TYPE (pDOC_TYPE IN VARCHAR2);
 
   /*************************************************************************
    NAME:       RESET_SEQ
    PURPOSE:    重置序號物件
    *************************************************************************/
   PROCEDURE RESET_SEQ (pNUM_SEQ_NAME IN VARCHAR2);
 
   /*************************************************************************
    NAME:       RESET_SEQ_ALL
    PURPOSE:    重置序號物件
    *************************************************************************/
   PROCEDURE RESET_SEQ_ALL;
   
   /*************************************************************************
    NAME:       REGEN_SEQ_BY_TYPE
    PURPOSE:    重新刪除/建立序號物件 by DOC_TYPE (See MY_DOCUMENT)
    *************************************************************************/
   PROCEDURE REGEN_SEQ_BY_TYPE (pDOC_TYPE IN VARCHAR2);
   
   /*************************************************************************
    NAME:       REGEN_SEQ
    PURPOSE:    重新刪除/建立序號物件
    *************************************************************************/
   PROCEDURE REGEN_SEQ (pNUM_SEQ_NAME IN VARCHAR2);
 
   /*************************************************************************
    NAME:       REGEN_SEQ_ALL
    PURPOSE:    刪除/建立序號物件 by DOC_TYPE (See MY_DOCUMENT)
    EXCEPTION:  If DOC_TYPE  not in MY_DOCUMENT.
    *************************************************************************/
   PROCEDURE REGEN_SEQ_ALL;
END MY_SEQ;
 
/*************************************************************************
PART-2
NAME:     MY_SEQ
PURPOSE:  處理序號物件 PACKAGE BODY
*************************************************************************/
create or replace
PACKAGE BODY MY_SEQ
IS
  /*************************************************************************
  NAME:       GET_NUM_SEQ_NAME
  PURPOSE:    回傳序號物件名稱 by DOC_TYPE (See MY_DOCUMENT)
  RETURN:     序號物件名稱
  EXCEPTION:  If DOC_TYPE  not in MY_DOCUMENT.
  *************************************************************************/
FUNCTION GET_NUM_SEQ_NAME
  (
    pDOC_TYPE IN VARCHAR2)
  RETURN VARCHAR2
IS
  RTNVAL VARCHAR2(100);
BEGIN
  -- select sequence object name
  SELECT DISTINCT NUM_SEQ_NAME
  INTO RTNVAL
  FROM MY_DOCUMENT
  WHERE DOC_TYPE = pDOC_TYPE;
  RETURN RTNVAL;
END;
 
 
/*************************************************************************
NAME:       NEXT_SEQ_BY_TYPE
PURPOSE:    回傳序號 by DOC_TYPE (See MY_DOCUMENT)
RETURN:     Default is (YYYYMMDDssss). If s>9999回傳(YYYYMMDDsssss)s不格式化.
EXCEPTION:  If DOC_TYPE  not in MY_DOCUMENT.
*************************************************************************/
FUNCTION NEXT_SEQ_BY_TYPE
  (
    pDOC_TYPE IN VARCHAR2)
  RETURN VARCHAR2
IS
  pNUM_SEQ_NAME VARCHAR2(100);
BEGIN
  pNUM_SEQ_NAME := GET_NUM_SEQ_NAME (pDOC_TYPE);
  RETURN NEXT_SEQ (pNUM_SEQ_NAME);
END;
/*************************************************************************
NAME:       NEXT_SEQ
PURPOSE:    回傳序號
RETURN:     Default is (YYYYMMDDssss). If s>9999回傳(YYYYMMDDsssss)s不格式化.
*************************************************************************/
FUNCTION NEXT_SEQ
  (
    pNUM_SEQ_NAME IN VARCHAR2)
  RETURN VARCHAR2
IS
  PRAGMA AUTONOMOUS_TRANSACTION;
  pNUM_SEQ NUMBER;
  pPREFIX  VARCHAR2 (6);
  pRTNVAL  VARCHAR2 (50);
BEGIN
  -- YYYYMMDD
  SELECT TO_CHAR (MYDATE, 'YYYYMMDD') INTO pPREFIX FROM DUAL;
  -- sequence number
  EXECUTE IMMEDIATE 'SELECT ' || pNUM_SEQ_NAME || '.NEXTVAL FROM DUAL' INTO pNUM_SEQ;
  -- format
  IF pNUM_SEQ > 9999 THEN
    pRTNVAL  := pPREFIX || TO_CHAR (pNUM_SEQ);
  ELSE
    pRTNVAL := pPREFIX || TO_CHAR (pNUM_SEQ, 'FM0000');
  END IF;
  RETURN pRTNVAL;
END;
/*************************************************************************
NAME:       RESET_SEQ_BY_TYPE
PURPOSE:    重置序號物件 by DOC_TYPE (See MY_DOCUMENT)
EXCEPTION:  If DOC_TYPE  not in MY_DOCUMENT.
*************************************************************************/
PROCEDURE RESET_SEQ_BY_TYPE
  (
    pDOC_TYPE IN VARCHAR2)
                  IS
  pNUM_SEQ_NAME VARCHAR2(100);
BEGIN
  pNUM_SEQ_NAME := GET_NUM_SEQ_NAME (pDOC_TYPE);
  RESET_SEQ (pNUM_SEQ_NAME);
END;
/*************************************************************************
NAME:       RESET_SEQ
PURPOSE:    重置序號物件
*************************************************************************/
PROCEDURE RESET_SEQ
  (
    pNUM_SEQ_NAME IN VARCHAR2)
                 IS
  PRAGMA AUTONOMOUS_TRANSACTION;
  pNUM_SEQ NUMBER;
BEGIN
  DBMS_OUTPUT.PUT_LINE('RESET SEQUENCE : ' || pNUM_SEQ_NAME);
  -- current sequence
  EXECUTE IMMEDIATE 'SELECT ' || pNUM_SEQ_NAME || '.NEXTVAL FROM DUAL' INTO pNUM_SEQ;
  -- set increment negative
  EXECUTE IMMEDIATE 'ALTER SEQUENCE ' || pNUM_SEQ_NAME || ' INCREMENT BY -' || pNUM_SEQ || ' MINVALUE 0';
  -- next sequence (reset to 0)
  EXECUTE IMMEDIATE 'SELECT ' || pNUM_SEQ_NAME || '.NEXTVAL FROM DUAL' INTO pNUM_SEQ;
  -- set increment positive
  EXECUTE IMMEDIATE 'ALTER SEQUENCE ' || pNUM_SEQ_NAME || ' INCREMENT BY 1 MINVALUE 0';
  COMMIT;
  
EXCEPTION
WHEN OTHERS THEN
  DBMS_OUTPUT.PUT_LINE (DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
  ROLLBACK;
  RAISE; -- reraise the current exception
END;
/*************************************************************************
NAME:       RESET_SEQ_ALL
PURPOSE:    重置序號物件
*************************************************************************/
PROCEDURE RESET_SEQ_ALL
IS
  CURSOR pCUR
  IS
    SELECT DISTINCT NUM_SEQ_NAME
    FROM MY_DOCUMENT
    WHERE NUM_SEQ_NAME IS NOT NULL;
  
  pNUM_SEQ_NAME_REC pCUR%ROWTYPE;
  pERROR_COUNT        NUMBER := 0;
  pERROR_NUM_SEQ_NAME VARCHAR2(2000) := '';
BEGIN
  
  OPEN pCUR;
  LOOP
    FETCH pCUR INTO pNUM_SEQ_NAME_REC;
    EXIT
  WHEN pCUR%NOTFOUND;
    BEGIN
      -- Reset sequence object
      RESET_SEQ (pNUM_SEQ_NAME_REC.NUM_SEQ_NAME);
    EXCEPTION
    WHEN OTHERS THEN
      pERROR_COUNT        := pERROR_COUNT + 1;
      pERROR_NUM_SEQ_NAME := pERROR_NUM_SEQ_NAME || ',' || pNUM_SEQ_NAME_REC.NUM_SEQ_NAME;
      DBMS_OUTPUT.PUT_LINE (DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
    END;
  END LOOP;
  CLOSE pCUR;
  IF pERROR_COUNT > 0 THEN
    RAISE_APPLICATION_ERROR ( -20999, 'Error Count : ' || TO_CHAR (pERROR_COUNT) || ' , OBJECT : ' || pERROR_NUM_SEQ_NAME);
  END IF;
END;
/*************************************************************************
NAME:       REGEN_SEQ_BY_TYPE
PURPOSE:    重新刪除/建立序號物件 by DOC_TYPE (See MY_DOCUMENT)
*************************************************************************/
PROCEDURE REGEN_SEQ_BY_TYPE
  (
    pDOC_TYPE IN VARCHAR2)
                  IS
  pNUM_SEQ_NAME VARCHAR2(100);
BEGIN
  pNUM_SEQ_NAME := GET_NUM_SEQ_NAME (pDOC_TYPE);
  REGEN_SEQ (pNUM_SEQ_NAME);
END;
/*************************************************************************
NAME:       REGEN_SEQ
PURPOSE:    重新刪除/建立序號物件
*************************************************************************/
PROCEDURE REGEN_SEQ
  (
    pNUM_SEQ_NAME IN VARCHAR2)
                 IS
  pDDL_STR VARCHAR2(1000);
BEGIN
  -- GET sequence object and remove start with argument. (cause the ddl include start with curval)
  SELECT DBMS_METADATA.GET_DDL ('SEQUENCE', pNUM_SEQ_NAME)
  INTO pDDL_STR
  FROM DUAL;
  SELECT REGEXP_REPLACE (pDDL_STR, 'START WITH \d+', 'START WITH 1')
  INTO pDDL_STR
  FROM DUAL;
  
  DBMS_OUTPUT.PUT_LINE(pDDL_STR);
  EXECUTE IMMEDIATE 'DROP SEQUENCE ' || pNUM_SEQ_NAME;
  EXECUTE IMMEDIATE pDDL_STR;
EXCEPTION
WHEN OTHERS THEN
  RAISE_APPLICATION_ERROR(-20999, DBMS_UTILITY.FORMAT_ERROR_BACKTRACE || ' >>>>' || pDDL_STR );
END;
/*************************************************************************
NAME:       REGEN_SEQ_ALL
PURPOSE:    刪除/建立序號物件 by DOC_TYPE (See MY_DOCUMENT)
EXCEPTION:  If DOC_TYPE  not in MY_DOCUMENT.
*************************************************************************/
PROCEDURE REGEN_SEQ_ALL
IS
  CURSOR pCUR
  IS
    SELECT DISTINCT NUM_SEQ_NAME
    FROM MY_DOCUMENT
    WHERE NUM_SEQ_NAME IS NOT NULL;
  
  pNUM_SEQ_NAME_REC pCUR%ROWTYPE;
  pERROR_COUNT        NUMBER := 0;
  pERROR_NUM_SEQ_NAME VARCHAR2(2000) := '';
BEGIN
  OPEN pCUR;
  LOOP
    FETCH pCUR INTO pNUM_SEQ_NAME_REC;
    EXIT
  WHEN pCUR%NOTFOUND;
    BEGIN
      -- Reset sequence object
      REGEN_SEQ (pNUM_SEQ_NAME_REC.NUM_SEQ_NAME);
    EXCEPTION
    WHEN OTHERS THEN
      pERROR_COUNT        := pERROR_COUNT + 1;
      pERROR_NUM_SEQ_NAME := pERROR_NUM_SEQ_NAME || ',' || pNUM_SEQ_NAME_REC.NUM_SEQ_NAME;
      DBMS_OUTPUT.PUT_LINE (DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
    END;
  END LOOP;
  CLOSE pCUR;
  IF pERROR_COUNT > 0 THEN
    RAISE_APPLICATION_ERROR ( -20999, 'Error Count : ' || TO_CHAR (pERROR_COUNT) || ' , OBJECT : ' || pERROR_NUM_SEQ_NAME);
  END IF;
END;
END MY_SEQ;
 
 
 
 
/*************************************************************************
PART-3
NAME:     TEST CASE
PURPOSE:  For Test
*************************************************************************/
GRANT CREATE SEQUENCE TO MYUSER; -- must execute before invoke REGEN
 
DECLARE
    VAL varchar2(50);
BEGIN
     DBMS_OUTPUT.PUT_LINE('--------' );
     VAL := MYUSER.MY_SEQ.GET_NUM_SEQ_NAME('BUTN') ;     
     DBMS_OUTPUT.PUT_LINE('GET_NUM_SEQ_NAME : ' || VAL );
     
     DBMS_OUTPUT.PUT_LINE('--------' );
     VAL := MYUSER.MY_SEQ.NEXT_SEQ('MY_BULLETIN_NUMBER_S');
     DBMS_OUTPUT.PUT_LINE('NEXT_SEQ : ' || VAL );
     
     DBMS_OUTPUT.PUT_LINE('--------' );
     VAL := MYUSER.MY_SEQ.NEXT_SEQ_BY_TYPE('BUTN');
     DBMS_OUTPUT.PUT_LINE('NEXT_SEQ_BY_TYPE : ' || VAL );
     
     DBMS_OUTPUT.PUT_LINE('--------' );
     MYUSER.MY_SEQ.RESET_SEQ('MY_BULLETIN_NUMBER_S');
     VAL := MYUSER.MY_SEQ.NEXT_SEQ('MY_BULLETIN_NUMBER_S');     
     DBMS_OUTPUT.PUT_LINE('RESET_SEQ > NEXT_SEQ : ' || VAL );      
 
     DBMS_OUTPUT.PUT_LINE('--------' );
     MYUSER.MY_SEQ.RESET_SEQ_BY_TYPE('BUTN');
     VAL := MYUSER.MY_SEQ.NEXT_SEQ_BY_TYPE('BUTN');     
     DBMS_OUTPUT.PUT_LINE('RESET_SEQ_BY_TYPE > NEXT_SEQ_BY_TYPE : ' || VAL );
     
     DBMS_OUTPUT.PUT_LINE('--------' );
     MYUSER.MY_SEQ.RESET_SEQ_ALL;
     VAL := MYUSER.MY_SEQ.NEXT_SEQ('MY_BULLETIN_NUMBER_S');     
     DBMS_OUTPUT.PUT_LINE('RESET_SEQ_ALL > NEXT_SEQ : ' || VAL );     
   
     DBMS_OUTPUT.PUT_LINE('--------' );
     MYUSER.MY_SEQ.REGEN_SEQ ('MY_BULLETIN_NUMBER_S');
     VAL := MYUSER.MY_SEQ.NEXT_SEQ('MY_BULLETIN_NUMBER_S');     
     DBMS_OUTPUT.PUT_LINE('REGEN_SEQ > NEXT_SEQ : ' || VAL );     
     
     
     DBMS_OUTPUT.PUT_LINE('--------' );
     MYUSER.MY_SEQ.REGEN_SEQ_BY_TYPE ('BUTN');
     VAL := MYUSER.MY_SEQ.NEXT_SEQ_BY_TYPE('BUTN');     
     DBMS_OUTPUT.PUT_LINE('REGEN_SEQ_BY_TYPE > NEXT_SEQ_BY_TYPE : ' || VAL );
     
     DBMS_OUTPUT.PUT_LINE('--------' );
     MYUSER.MY_SEQ.REGEN_SEQ_ALL;
     VAL := MYUSER.MY_SEQ.NEXT_SEQ('MY_BULLETIN_NUMBER_S');     
     DBMS_OUTPUT.PUT_LINE('REGEN_SEQ_ALL > NEXT_SEQ : ' || VAL );
END;