如何在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;
这是我的做法:
- 放下序列
- 重新创建它
例:
--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。 遵循以下步骤。 按照以下所示顺序执行所有步骤:
-
ALTER SEQUENCE TESTSEQ INCREMENT BY -3;
-
SELECT TESTSEQ.NEXTVAL FROM dual
-
ALTER SEQUENCE TESTSEQ INCREMENT BY 1;
-
SELECT TESTSEQ.NEXTVAL FROM dual
改变序列的INCREMENT值,增加它,然后改变它是非常痛苦的,再加上你不必重新建立所有的授权,就像你丢弃/重新创建序列一样。
还有一种方法可以在Oracle中重置序列:设置maxvalue
和cycle
属性。 当序列的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_value
和min_value
和min_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;
请注意,如果序列滞后,上面的代码将起作用。
以下是如何使所有自动递增序列与实际数据匹配:
-
创建一个过程来执行下一个值,就像这个线程已经描述的那样:
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_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;
笔记:
- 过程从触发器代码中提取名称,不依赖于命名约定
- 要在执行前检查生成的代码,请在最后两行切换注释
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。