如何在Oracle中重置序列?

在PostgreSQL中 ,我可以这样做:

ALTER SEQUENCE serial RESTART WITH 0; 

有没有Oracle的等价物?

Oracle大师Tom Kyte将这个序列重置为0是一个很好的过程。 关于下面的链接的利弊大讨论了。

 tkyte@TKYTE901.US.ORACLE.COM> create or replace procedure reset_seq( p_seq_name in varchar2 ) is l_val number; begin execute immediate 'select ' || p_seq_name || '.nextval from dual' INTO l_val; execute immediate 'alter sequence ' || p_seq_name || ' increment by -' || l_val || ' minvalue 0'; execute immediate 'select ' || p_seq_name || '.nextval from dual' INTO l_val; execute immediate 'alter sequence ' || p_seq_name || ' increment by 1 minvalue 0'; end; / 

从此页面: 动态SQL重置序列值
另一个好的讨论也在这里: 如何重置序列?

真正的重启不可能AFAIK 。 (请纠正我,如果我错了!)。

但是,如果您想将其设置为0,则可以删除并重新创建它。

如果要将其设置为特定值,可以将INCREMENT设置为负值并获取下一个值。

也就是说,如果你的序列是500,你可以通过设置为100

 ALTER SEQUENCE serial INCREMENT BY -400; SELECT serial.NEXTVAL FROM dual; ALTER SEQUENCE serial INCREMENT BY 1; 

这是我的做法:

  1. 放下序列
  2. 重新创建它

例:

 --Drop sequence DROP SEQUENCE MY_SEQ; -- Create sequence create sequence MY_SEQ minvalue 1 maxvalue 999999999999999999999 start with 1 increment by 1 cache 20; 

我的方法是Dougman的例子的一个十分延伸。

扩展名是…

传入种子值作为参数。 为什么? 我喜欢打电话重新设置序列回到某些表中使用的最大ID 。 我最终从另一个执行多个调用序列的脚本中调用这个过程,将nextval重新设置到某个级别,这个级别足够高,不会导致主键冲突,因为我将序列值用于唯一标识符。

它也尊重以前的minvalue 。 如果所需的p_val现有最小值高于当前值或计算的下一个值,它实际上可能将下一个值推高

最重要的是,它可以被调用重置为一个指定的值,并等待,直到你看到包装“修复我所有的序列”过程结束。

 create or replace procedure Reset_Sequence( p_seq_name in varchar2, p_val in number default 0) is l_current number := 0; l_difference number := 0; l_minvalue user_sequences.min_value%type := 0; begin select min_value into l_minvalue from user_sequences where sequence_name = p_seq_name; execute immediate 'select ' || p_seq_name || '.nextval from dual' INTO l_current; if p_Val < l_minvalue then l_difference := l_minvalue - l_current; else l_difference := p_Val - l_current; end if; if l_difference = 0 then return; end if; execute immediate 'alter sequence ' || p_seq_name || ' increment by ' || l_difference || ' minvalue ' || l_minvalue; execute immediate 'select ' || p_seq_name || '.nextval from dual' INTO l_difference; execute immediate 'alter sequence ' || p_seq_name || ' increment by 1 minvalue ' || l_minvalue; end Reset_Sequence; 

该过程本身是有用的,但现在让我们添加另一个调用它,并以序列命名约定以编程方式指定一切,并寻找现有表/字段中使用的最大值…

 create or replace procedure Reset_Sequence_to_Data( p_TableName varchar2, p_FieldName varchar2 ) is l_MaxUsed NUMBER; BEGIN execute immediate 'select coalesce(max(' || p_FieldName || '),0) from '|| p_TableName into l_MaxUsed; Reset_Sequence( p_TableName || '_' || p_Fieldname || '_SEQ', l_MaxUsed ); END Reset_Sequence_to_Data; 

现在我们正在用煤气做饭!

上面的程序将检查一个表中字段的最大值,从表/字段对中建立一个序列名称并用所检测到的最大值调用“Reset_Sequence”

接下来是这个谜题的最后一部分和蛋糕上的糖霜。

 create or replace procedure Reset_All_Sequences is BEGIN Reset_Sequence_to_Data( 'ACTIVITYLOG', 'LOGID' ); Reset_Sequence_to_Data( 'JOBSTATE', 'JOBID' ); Reset_Sequence_to_Data( 'BATCH', 'BATCHID' ); END Reset_All_Sequences; 

在我的实际数据库中,有大约一百个其他序列通过这个机制被重置,所以在上面的程序中有97个更多的对Reset_Sequence_to_Data的调用。

爱它? 讨厌它? 冷漠?

 alter sequence serial restart start with 0; 

这个特性在Oracle 12c中是新的。 这包括在官方文件中 。 我在Oracle包DBMS_METADATA_DIFF生成的脚本中找到它。

以下脚本将序列设置为所需的值:

给定一个新创建的序列,名为PCS_PROJ_KEY_SEQ和表PCS_PROJ:

 BEGIN DECLARE PROJ_KEY_MAX NUMBER := 0; PROJ_KEY_CURRVAL NUMBER := 0; BEGIN SELECT MAX (PROJ_KEY) INTO PROJ_KEY_MAX FROM PCS_PROJ; EXECUTE IMMEDIATE 'ALTER SEQUENCE PCS_PROJ_KEY_SEQ INCREMENT BY ' || PROJ_KEY_MAX; SELECT PCS_PROJ_KEY_SEQ.NEXTVAL INTO PROJ_KEY_CURRVAL FROM DUAL; EXECUTE IMMEDIATE 'ALTER SEQUENCE PCS_PROJ_KEY_SEQ INCREMENT BY 1'; END; END; / 

这个存储过程重新启动我的序列:

 Create or Replace Procedure Reset_Sequence is SeqNbr Number; begin /* Reset Sequence 'seqXRef_RowID' to 0 */ Execute Immediate 'Select seqXRef.nextval from dual ' Into SeqNbr; Execute Immediate 'Alter sequence seqXRef increment by - ' || TO_CHAR(SeqNbr) ; Execute Immediate 'Select seqXRef.nextval from dual ' Into SeqNbr; Execute Immediate 'Alter sequence seqXRef increment by 1'; END; 

/

1)假设你创建一个SEQUENCE,如下所示:

 CREATE SEQUENCE TESTSEQ INCREMENT BY 1 MINVALUE 1 MAXVALUE 500 NOCACHE NOCYCLE NOORDER 

2)现在你从SEQUENCE获取值。 可以说我已经提取了四次,如下所示。

 SELECT TESTSEQ.NEXTVAL FROM dual SELECT TESTSEQ.NEXTVAL FROM dual SELECT TESTSEQ.NEXTVAL FROM dual SELECT TESTSEQ.NEXTVAL FROM dual 

3)执行以上四个命令后,SEQUENCE的值将是4.现在假设我已经将SEQUENCE的值重新设置为1。 遵循以下步骤。 按照以下所示顺序执行所有步骤:

  1. ALTER SEQUENCE TESTSEQ INCREMENT BY -3;
  2. SELECT TESTSEQ.NEXTVAL FROM dual
  3. ALTER SEQUENCE TESTSEQ INCREMENT BY 1;
  4. SELECT TESTSEQ.NEXTVAL FROM dual

改变序列的INCREMENT值,增加它,然后改变它是非常痛苦的,再加上你不必重新建立所有的授权,就像你丢弃/重新创建序列一样。

还有一种方法可以在Oracle中重置序列:设置maxvaluecycle属性。 当序列的nextval碰到maxvalue ,如果设置了cycle属性,那么它将从序列的minvalue开始。

与设置负increment by相比,此方法的优点是在重置过程运行时可以继续使用该序列,从而减少了需要采取某种形式的中断进行重置的机会。

maxvalue的值必须大于当前的nextval ,所以下面的过程包含一个可选参数,以允许在选择过程中的nextval和设置cycle属性之间重新访问序列的情况下使用缓冲区。

 create sequence s start with 1 increment by 1; select s.nextval from dual connect by level <= 20; NEXTVAL ---------- 1 ... 20 create or replace procedure reset_sequence ( i_buffer in pls_integer default 0) as maxval pls_integer; begin maxval := s.nextval + greatest(i_buffer, 0); --ensure we don't go backwards! execute immediate 'alter sequence s cycle minvalue 0 maxvalue ' || maxval; maxval := s.nextval; execute immediate 'alter sequence s nocycle maxvalue 99999999999999'; end; / show errors exec reset_sequence; select s.nextval from dual; NEXTVAL ---------- 1 

作为立场的程序仍然允许另一个会话将获取值0的可能性,这可能或可能不是你的问题。 如果是这样,你总是可以:

  • 在第一个改变中设置minvalue 1
  • 排除第二个nextval抓取
  • 移动语句以将nocycle属性设置为另一个过程,以便稍后运行(假设您要这样做)。

我创建一个块来重置我所有的序列:

 DECLARE I_val number; BEGIN FOR US IN (SELECT US.SEQUENCE_NAME FROM USER_SEQUENCES US) LOOP execute immediate 'select ' || US.SEQUENCE_NAME || '.nextval from dual' INTO l_val; execute immediate 'alter sequence ' || US.SEQUENCE_NAME || ' increment by -' || l_val || ' minvalue 0'; execute immediate 'select ' || US.SEQUENCE_NAME || '.nextval from dual' INTO l_val; execute immediate 'alter sequence ' || US.SEQUENCE_NAME || ' increment by 1 minvalue 0'; END LOOP; END; 

下面是一个更强大的过程,用于修改序列返回的下一个值,另外还有更多。

  • 首先它防止SQL注入攻击,因为没有任何字符串被用来直接创建任何动态SQL语句,
  • 其次,它防止将下一个序列值设置在最小或最大序列值的范围之外。 next_value将是!= min_valuemin_valuemin_value之间。
  • 第三,清理时需要考虑当前(或提议)的increment_by设置以及所有其他的顺序设置。
  • 第四,除第一个以外的所有参数都是可选的,除非指定将当前序列设置作为默认值。 如果没有指定可选参数,则不采取任何操作。
  • 最后,如果尝试更改不存在的序列(或不是当前用户拥有的序列),则会引发ORA-01403: no data found错误。

代码如下:

 CREATE OR REPLACE PROCEDURE alter_sequence( seq_name user_sequences.sequence_name%TYPE , next_value user_sequences.last_number%TYPE := null , increment_by user_sequences.increment_by%TYPE := null , min_value user_sequences.min_value%TYPE := null , max_value user_sequences.max_value%TYPE := null , cycle_flag user_sequences.cycle_flag%TYPE := null , cache_size user_sequences.cache_size%TYPE := null , order_flag user_sequences.order_flag%TYPE := null) AUTHID CURRENT_USER AS l_seq user_sequences%rowtype; l_old_cache user_sequences.cache_size%TYPE; l_next user_sequences.min_value%TYPE; BEGIN -- Get current sequence settings as defaults SELECT * INTO l_seq FROM user_sequences WHERE sequence_name = seq_name; -- Update target settings l_old_cache := l_seq.cache_size; l_seq.increment_by := nvl(increment_by, l_seq.increment_by); l_seq.min_value := nvl(min_value, l_seq.min_value); l_seq.max_value := nvl(max_value, l_seq.max_value); l_seq.cycle_flag := nvl(cycle_flag, l_seq.cycle_flag); l_seq.cache_size := nvl(cache_size, l_seq.cache_size); l_seq.order_flag := nvl(order_flag, l_seq.order_flag); IF next_value is NOT NULL THEN -- Determine next value without exceeding limits l_next := LEAST(GREATEST(next_value, l_seq.min_value+1),l_seq.max_value); -- Grab the actual latest seq number EXECUTE IMMEDIATE 'ALTER SEQUENCE '||l_seq.sequence_name || ' INCREMENT BY 1' || ' MINVALUE '||least(l_seq.min_value,l_seq.last_number-l_old_cache) || ' MAXVALUE '||greatest(l_seq.max_value,l_seq.last_number) || ' NOCACHE' || ' ORDER'; EXECUTE IMMEDIATE 'SELECT '||l_seq.sequence_name||'.NEXTVAL FROM DUAL' INTO l_seq.last_number; l_next := l_next-l_seq.last_number-1; -- Reset the sequence number IF l_next <> 0 THEN EXECUTE IMMEDIATE 'ALTER SEQUENCE '||l_seq.sequence_name || ' INCREMENT BY '||l_next || ' MINVALUE '||least(l_seq.min_value,l_seq.last_number) || ' MAXVALUE '||greatest(l_seq.max_value,l_seq.last_number) || ' NOCACHE' || ' ORDER'; EXECUTE IMMEDIATE 'SELECT '||l_seq.sequence_name||'.NEXTVAL FROM DUAL' INTO l_next; END IF; END IF; -- Prepare Sequence for next use. IF COALESCE( cycle_flag , next_value , increment_by , min_value , max_value , cache_size , order_flag) IS NOT NULL THEN EXECUTE IMMEDIATE 'ALTER SEQUENCE '||l_seq.sequence_name || ' INCREMENT BY '||l_seq.increment_by || ' MINVALUE '||l_seq.min_value || ' MAXVALUE '||l_seq.max_value || CASE l_seq.cycle_flag WHEN 'Y' THEN ' CYCLE' ELSE ' NOCYCLE' END || CASE l_seq.cache_size WHEN 0 THEN ' NOCACHE' ELSE ' CACHE '||l_seq.cache_size END || CASE l_seq.order_flag WHEN 'Y' THEN ' ORDER' ELSE ' NOORDER' END; END IF; END; 

在我的项目中,一旦有人手动输入记录而不使用序列,因此我必须手动重置序列值,为此我在下面的sql代码片段中写了:

 declare max_db_value number(10,0); cur_seq_value number(10,0); counter number(10,0); difference number(10,0); dummy_number number(10); begin -- enter table name here select max(id) into max_db_value from persons; -- enter sequence name here select last_number into cur_seq_value from user_sequences where sequence_name = 'SEQ_PERSONS'; difference := max_db_value - cur_seq_value; for counter in 1..difference loop -- change sequence name here as well select SEQ_PERSONS.nextval into dummy_number from dual; end loop; end; 

请注意,如果序列滞后,上面的代码将起作用。

以下是如何使所有自动递增序列与实际数据匹配:

  1. 创建一个过程来执行下一个值,就像这个线程已经描述的那样:

     CREATE OR REPLACE PROCEDURE Reset_Sequence( P_Seq_Name IN VARCHAR2, P_Val IN NUMBER DEFAULT 0) IS L_Current NUMBER := 0; L_Difference NUMBER := 0; L_Minvalue User_Sequences.Min_Value%Type := 0; BEGIN SELECT Min_Value INTO L_Minvalue FROM User_Sequences WHERE Sequence_Name = P_Seq_Name; EXECUTE Immediate 'select ' || P_Seq_Name || '.nextval from dual' INTO L_Current; IF P_Val < L_Minvalue THEN L_Difference := L_Minvalue - L_Current; ELSE L_Difference := P_Val - L_Current; END IF; IF L_Difference = 0 THEN RETURN; END IF; EXECUTE Immediate 'alter sequence ' || P_Seq_Name || ' increment by ' || L_Difference || ' minvalue ' || L_Minvalue; EXECUTE Immediate 'select ' || P_Seq_Name || '.nextval from dual' INTO L_Difference; EXECUTE Immediate 'alter sequence ' || P_Seq_Name || ' increment by 1 minvalue ' || L_Minvalue; END Reset_Sequence; 
  2. 创建另一个过程来协调所有序列与实际内容:

     CREATE OR REPLACE PROCEDURE RESET_USER_SEQUENCES_TO_DATA IS STMT CLOB; BEGIN SELECT 'select ''BEGIN'' || chr(10) || x || chr(10) || ''END;'' FROM (select listagg(x, chr(10)) within group (order by null) x FROM (' || X || '))' INTO STMT FROM (SELECT LISTAGG(X, ' union ') WITHIN GROUP ( ORDER BY NULL) X FROM (SELECT CHR(10) || 'select ''Reset_Sequence(''''' || SEQ_NAME || ''''','' || coalesce(max(' || COL_NAME || '), 0) || '');'' x from ' || TABLE_NAME X FROM (SELECT TABLE_NAME, REGEXP_SUBSTR(WTEXT, 'NEW\.(\S*) IS NULL',1,1,'i',1) COL_NAME, REGEXP_SUBSTR(BTEXT, '(\.|\s)([a-z_]*)\.nextval',1,1,'i',2) SEQ_NAME FROM USER_TRIGGERS LEFT JOIN (SELECT NAME BNAME, TEXT BTEXT FROM USER_SOURCE WHERE TYPE = 'TRIGGER' AND UPPER(TEXT) LIKE '%NEXTVAL%' ) ON BNAME = TRIGGER_NAME LEFT JOIN (SELECT NAME WNAME, TEXT WTEXT FROM USER_SOURCE WHERE TYPE = 'TRIGGER' AND UPPER(TEXT) LIKE '%IS NULL%' ) ON WNAME = TRIGGER_NAME WHERE TRIGGER_TYPE = 'BEFORE EACH ROW' AND TRIGGERING_EVENT = 'INSERT' ) ) ) ; EXECUTE IMMEDIATE STMT INTO STMT; --dbms_output.put_line(stmt); EXECUTE IMMEDIATE STMT; END RESET_USER_SEQUENCES_TO_DATA; 

笔记:

  1. 过程从触发器代码中提取名称,不依赖于命名约定
  2. 要在执行前检查生成的代码,请在最后两行切换注释

Jezus,所有这些只是索引重新开始编程…也许我是一个白痴,但对于pre-oracle 12(它有一个重新启动功能),什么是错误的一个simpel:

 drop sequence blah; create sequence blah 

我做了一个替代,用户不需要知道值,系统得到并使用变量来更新。

 --Atualizando sequence da tabela SIGA_TRANSACAO, pois está desatualizada DECLARE actual_sequence_number INTEGER; max_number_from_table INTEGER; difference INTEGER; BEGIN SELECT [nome_da_sequence].nextval INTO actual_sequence_number FROM DUAL; SELECT MAX([nome_da_coluna]) INTO max_number_from_table FROM [nome_da_tabela]; SELECT (max_number_from_table-actual_sequence_number) INTO difference FROM DUAL; IF difference > 0 then EXECUTE IMMEDIATE CONCAT('alter sequence [nome_da_sequence] increment by ', difference); --aqui ele puxa o próximo valor usando o incremento necessário SELECT [nome_da_sequence].nextval INTO actual_sequence_number from dual; --aqui volta o incremento para 1, para que futuras inserções funcionem normalmente EXECUTE IMMEDIATE 'ALTER SEQUENCE [nome_da_sequence] INCREMENT by 1'; DBMS_OUTPUT.put_line ('A sequence [nome_da_sequence] foi atualizada.'); ELSE DBMS_OUTPUT.put_line ('A sequence [nome_da_sequence] NÃO foi atualizada, já estava OK!'); END IF; END; 

您可以使用CYCLE选项,如下所示:

 CREATE SEQUENCE test_seq MINVALUE 0 MAXVALUE 100 START WITH 0 INCREMENT BY 1 CYCLE; 

在这种情况下,当序列达到MAXVALUE(100)时,它将循环到MINVALUE(0)。

在序列递减的情况下,序列将循环到MAXVALUE。