1. 程式人生 > 其它 >關於reset sequence(r3筆記第85天)

關於reset sequence(r3筆記第85天)

sequence在工作中使用比較頻繁,對於Insert中插入的值,如果只需要它能夠自動遞增,這個時候sequence就派上用場了。 但是既然sequence的值需要遞增就有可能會達到最大值。比如sequence從1開始遞增,遞增幅度為1,最大值為100,那麼很快就會達到最大值。我們可以指定sequence的值。這個時候可以使用刪除,重建的方式,但是這種方式相對來說影響範圍較大,相關的儲存過程,函式,同義詞都會失效。可以通過更為靈活的方式來reset sequence. 一種方式相對直接,簡潔。就是修改sequence的屬性,把increment的值調高點,這樣每次遞增的幅度就大,然後迴圈遞增,知道遞增的值接近目標值,然後修改Increment的值為1. pl/sql的實現如下,比如我們要修改account_1sq的值,把它修改為10001000,就可以這樣來修改。

alter sequence ACCOUNT_1SQ increment by 69 nocache;
alter sequence ACCOUNT_1SQ increment by 1 nocache;
declare
  LastValue integer;
begin
  loop
    select ACCOUNT_1SQ.currval into LastValue from dual;
    exit when LastValue >= 10001000 - 1;
    select ACCOUNT_1SQ.nextval into LastValue from dual;
  end loop;
end;
/
alter sequence ACCOUNT_1SQ increment by 1 cache 20;

當然了這種方式還是存在不少的缺點。 一來是耦合度較高。在程式碼裡面嵌入了太多的細節。pl/sql指令碼從頭到尾都是account_1sq相關。 reset的值和遞增的幅度也得考慮周到。要不就可能出問題,導致reset的值達不到要求。 如果要修改序列的一些屬性,在最後需要恢復。所以我們得時刻記得sequence的細節資訊。 可以使用下面的改進指令碼來修復上面的不足。這個指令碼需要幾個引數。物件型別(比如sequence或者table),物件名稱(sequence的名稱),sequence的值(需要修改的值)

WHENEVER SQLERROR EXIT 5
DEFINE OBJTYPE="&1"
DEFINE OBJNAME="&2"
DEFINE SEQVALUE="&3"
SET ECHO OFF
SET FEEDBACK OFF
SET SERVEROUTPUT ON
SET LINESIZE 100
SET PAGESIZE 0
SET TERMOUT ON
SET VERIFY OFF


DECLARE CURSOR C1 IS
 SELECT
        DISTINCT SQ.SEQUENCE_NAME
   FROM
        USER_SEQUENCES    SQ,
        USER_COL_COMMENTS TB
  WHERE 
        TB.COMMENTS = SQ.SEQUENCE_NAME
    AND UPPER('&OBJTYPE') = 'TABLE'
    AND TB.TABLE_NAME = UPPER('&OBJNAME') 
 UNION
 SELECT
       UPPER('&OBJNAME') SEQUENCE_NAME
   FROM
       DUAL
  WHERE
       UPPER('&OBJTYPE') = 'SEQUENCE' ;
sql_tab_name  USER_COL_COMMENTS.TABLE_NAME%TYPE ;
sql_col_name  USER_COL_COMMENTS.COLUMN_NAME%TYPE ;
sql_seq_name  USER_SEQUENCES.SEQUENCE_NAME%TYPE ;
old_val       NUMBER := 0;
new_val       NUMBER := 0;
max_val       NUMBER := 0;
seq_max_val   NUMBER := 0;
old_min_val   NUMBER := 0;
old_inc       NUMBER := 0;
new_inc       NUMBER := 0;
seq_cur       INTEGER ;
seq_fld       INTEGER ;
seq_val       INTEGER ;
seq_cur_rows  INTEGER ;
seq_fld_rows  INTEGER ;
seq_val_rows  INTEGER ;
found_seq_ind CHAR(1) := 'Y' ;
old_new_diff  INTEGER ;
seq_cycle     CHAR(1) := 'N' ;
BEGIN
DBMS_OUTPUT.ENABLE(2000000);
OPEN C1;
LOOP
FETCH C1 INTO
sql_seq_name ;
EXIT WHEN C1%NOTFOUND ;
BEGIN
found_seq_ind := 'Y' ;
SELECT
      MIN_VALUE,
      MAX_VALUE,
      INCREMENT_BY,
      CYCLE_FLAG
  INTO
      old_min_val,
      seq_max_val,
      old_inc,
      seq_cycle
  FROM
      USER_SEQUENCES
 WHERE
      SEQUENCE_NAME = sql_seq_name ;

EXCEPTION
WHEN NO_DATA_FOUND THEN
     DBMS_OUTPUT.PUT_LINE('Sequence: '||sql_seq_name||' is not exists.');
     found_seq_ind := 'N' ;
WHEN OTHERS THEN
     EXIT ;
END ;

------------------------------------------------------------
--Get Max val from all tables that related to this sequence.
------------------------------------------------------------

IF ( found_seq_ind = 'Y' )
THEN
    IF ( UPPER('&OBJTYPE') = 'TABLE' )
    THEN
        seq_fld := DBMS_SQL.OPEN_CURSOR ;
        DBMS_SQL.PARSE (seq_fld, 'SELECT COLUMN_NAME,TABLE_NAME FROM USER_COL_COMMENTS '||
                                 ' WHERE COMMENTS '||' = '||':sql1 ',DBMS_SQL.V7);
        DBMS_SQL.BIND_VARIABLE(seq_fld, 'sql1' ,sql_seq_name);
        DBMS_SQL.DEFINE_COLUMN (seq_fld, 1, sql_col_name, 30 );
        DBMS_SQL.DEFINE_COLUMN (seq_fld, 2, sql_tab_name, 30 );
        seq_fld_rows := DBMS_SQL.EXECUTE (seq_fld);
        max_val := old_min_val ;

        LOOP
 
            IF DBMS_SQL.FETCH_ROWS (seq_fld) > 0
            THEN
                DBMS_SQL.COLUMN_VALUE ( seq_fld, 1, sql_col_name ) ;
                DBMS_SQL.COLUMN_VALUE ( seq_fld, 2, sql_tab_name ) ;
                seq_val := DBMS_SQL.OPEN_CURSOR ;
                DBMS_SQL.PARSE (seq_val, 'SELECT GREATEST(NVL(MAX('||sql_col_name||'),0),'||max_val||') FROM '||
                                         sql_tab_name|| ' WHERE '||sql_col_name||
                                         ' NOT IN (999999999,888888888) ',DBMS_SQL.V7);
                DBMS_SQL.DEFINE_COLUMN (seq_val, 1, max_val);
                seq_val_rows := DBMS_SQL.EXECUTE (seq_val);
 
                IF DBMS_SQL.FETCH_ROWS (seq_val) > 0
                THEN
                    DBMS_SQL.COLUMN_VALUE ( seq_val, 1, max_val ) ;
                END IF ;
                DBMS_SQL.CLOSE_CURSOR (seq_val) ;
            ELSE
                DBMS_SQL.CLOSE_CURSOR (seq_fld) ;
                EXIT ;
            END IF ;
 
        END LOOP ;
ELSE
    max_val := &SEQVALUE ;
END IF ;

------------------------------------------------------------
-- Foreach sequence get the nextvalue.                    --
------------------------------------------------------------

seq_cur := DBMS_SQL.OPEN_CURSOR ;
DBMS_SQL.PARSE (seq_cur, 'SELECT '||sql_seq_name||'.NEXTVAL FROM DUAL',DBMS_SQL.V7);
DBMS_SQL.DEFINE_COLUMN (seq_cur, 1, old_val );
seq_cur_rows := DBMS_SQL.EXECUTE (seq_cur);

IF DBMS_SQL.FETCH_ROWS (seq_cur) > 0
THEN
    DBMS_SQL.COLUMN_VALUE ( seq_cur, 1, old_val );
    DBMS_SQL.CLOSE_CURSOR (seq_cur);
ELSE
    DBMS_SQL.CLOSE_CURSOR (seq_cur);
END IF ;

------------------------------------------------------------
-- Select sequences differences.                          --
------------------------------------------------------------

IF ( max_val > old_val )
THEN
    old_new_diff :=  max_val - old_val ;
ELSE
    old_new_diff :=  max_val - ( old_val - old_min_val  );
END IF;


------------------------------------------------------------
-- Foreach sequence set the nextvalue with the new increment.
------------------------------------------------------------
IF ( old_new_diff <> 0 ) AND ( (old_new_diff + old_val) < seq_max_val )
THEN
    ---------------------------------------------
    --setting NOCYCLE for the sequence allow us
    --to set sequence on MIN_VAL.
    ---------------------------------------------
    IF ( seq_cycle = 'Y' )
    THEN
        seq_cur := DBMS_SQL.OPEN_CURSOR ;
        DBMS_SQL.PARSE (seq_cur, 'ALTER SEQUENCE '||sql_seq_name||' NOCYCLE ',DBMS_SQL.V7);
        seq_cur_rows := DBMS_SQL.EXECUTE (seq_cur);
        DBMS_SQL.CLOSE_CURSOR (seq_cur);
    END IF;
    ---------------------------------------------
    --set the nextvalue with the new increment.
    ---------------------------------------------
    seq_cur := DBMS_SQL.OPEN_CURSOR ;
    DBMS_SQL.PARSE (seq_cur, 'ALTER SEQUENCE '||sql_seq_name||' INCREMENT BY '||old_new_diff||' ',DBMS_SQL.V7);
    seq_cur_rows := DBMS_SQL.EXECUTE (seq_cur);
    DBMS_SQL.CLOSE_CURSOR (seq_cur);


    ---------------------------------------------
    --Get the nextvalue with the new increment.
    ---------------------------------------------
    seq_cur := DBMS_SQL.OPEN_CURSOR ;
    DBMS_SQL.PARSE (seq_cur, 'SELECT '||sql_seq_name||'.NEXTVAL FROM DUAL',DBMS_SQL.V7);
    DBMS_SQL.DEFINE_COLUMN (seq_cur, 1, new_val );
    seq_cur_rows := DBMS_SQL.EXECUTE (seq_cur);
    IF DBMS_SQL.FETCH_ROWS (seq_cur) > 0
    THEN
        DBMS_SQL.COLUMN_VALUE ( seq_cur, 1, new_val );
        DBMS_SQL.CLOSE_CURSOR (seq_cur);
    ELSE
        DBMS_SQL.CLOSE_CURSOR (seq_cur);
    END IF ;


    DBMS_OUTPUT.PUT_LINE('Resetting sequence: '||sql_seq_name||' to: '||new_val);

    --------------------------------------------------
    --set the nextvalue with the original increment.
    --------------------------------------------------
    seq_cur := DBMS_SQL.OPEN_CURSOR ;
    DBMS_SQL.PARSE (seq_cur, 'ALTER SEQUENCE '||sql_seq_name||' INCREMENT BY '||old_inc||' ',DBMS_SQL.V7);
    seq_cur_rows := DBMS_SQL.EXECUTE (seq_cur);
    DBMS_SQL.CLOSE_CURSOR (seq_cur);

    ---------------------------------------------
    --setting original CYCLE for the sequence  --
    ---------------------------------------------


    IF ( seq_cycle = 'Y' )
    THEN
        seq_cur := DBMS_SQL.OPEN_CURSOR ;
        DBMS_SQL.PARSE (seq_cur, 'ALTER SEQUENCE '||sql_seq_name||' CYCLE ',DBMS_SQL.V7);
        seq_cur_rows := DBMS_SQL.EXECUTE (seq_cur);
        DBMS_SQL.CLOSE_CURSOR (seq_cur);
    END IF;

END IF ;

END IF;

END LOOP ;

CLOSE C1 ;

END;
/

執行指令碼的時候,比如指令碼名稱為reset_seq.sql 可以這樣執行。 @reset_seq.sql sequence account_1sq 10000010