關於reset sequence(r3筆記第85天)
阿新 • • 發佈:2022-05-04
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