I have a sequence used to seed my (Integer based) primary keys in an oracle table.
It appears this sequence has not always been used to insert new values into the table. How do I get the sequence back in step with the actual values in the table?
I have a sequence used to seed my (Integer based) primary keys in an oracle table.
It appears this sequence has not always been used to insert new values into the table. How do I get the sequence back in step with the actual values in the table?
If ID is the name of your PK column and PK_SEQ is the name of your sequence:
Find the value of the highest PK by SELECT MAX(ID) FROM tableName
Find the value of the next PK_SEQ by SELECT PK_SEQ.NEXTVAL FROM DUAL
Bump the sequence by SELECT PK_SEQ.NEXTVAL FROM DUAL
Reset the sequence increment value to 1 by ALTER SEQUENCE PK_SEQ INCREMENT BY 1
This all assumes that you don't have new inserts into the table while you're doing this...
In short, game it:
-- Current sequence value is 1000
ALTER SEQUENCE x INCREMENT BY -999;
Sequence altered.
SELECT X.NEXTVAL FROM DUAL;
1
ALTER SEQUENCE x INCREMENT BY 1;
Sequence altered.
You can get the max sequence value used within your table, do the math, and update the sequence accordingly.
Declare
difference INTEGER;
sqlstmt varchar2(255);
sequenceValue Number;
begin
sqlstmt := 'ALTER SEQUENCE YOURSEQUENCE INCREMENT BY ';
select YOURSEQUENCE.NEXTVAL into sequenceValue from dual;
select (nvl(Max(YOURID),0) - sequenceValue)+1 into difference from YOURTABLE;
if difference > 0 then
EXECUTE IMMEDIATE sqlstmt || difference;
select YOURSEQUENCE.NEXTVAL INTO sequenceValue from dual;
EXECUTE IMMEDIATE sqlstmt || 1;
end if;
end;
select YOURSEQUENCE.NEXTVAL into sequenceValue from dual;
at line 7, you can check an alternative for it at my answer at https://mcmap.net/q/614264/-oracle-9-resetting-sequence-to-match-the-state-of-the-table, that fetches all_sequences.last_number WHERE sequence_name = 'YOURSEQUENCE'
instead of YOURSEQUENCE.NEXTVAL
, probably getting the desired value given sequence value caching being disabled. –
Centaury I made this script as I did not find a script online that dynamically sets all my sequences to the current highest ID. Tested on Oracle 11.2.0.4.
DECLARE
difference INTEGER;
sqlstmt VARCHAR2(255) ;
sqlstmt2 VARCHAR2(255) ;
sqlstmt3 VARCHAR2(255) ;
sequenceValue NUMBER;
sequencename VARCHAR2(30) ;
sequencelastnumber INTEGER;
CURSOR allseq
IS
SELECT sequence_name, last_number FROM user_sequences ORDER BY sequence_name;
BEGIN
DBMS_OUTPUT.enable(32000) ;
OPEN allseq;
LOOP
FETCH allseq INTO sequencename, sequencelastnumber;
EXIT
WHEN allseq%NOTFOUND;
sqlstmt := 'ALTER SEQUENCE ' || sequencename || ' INCREMENT BY ';
--Assuming: <tablename>_id is <sequencename>
sqlstmt2 := 'select (nvl(Max(ID),0) - :1)+1 from ' || SUBSTR(sequencename, 1, LENGTH(sequencename) - 3) ;
--DBMS_OUTPUT.PUT_LINE(sqlstmt2);
--Attention: makes use of user_sequences.last_number --> possible cache problems!
EXECUTE IMMEDIATE sqlstmt2 INTO difference USING sequencelastnumber;
IF difference > 0 THEN
DBMS_OUTPUT.PUT_LINE('EXECUTE IMMEDIATE ' || sqlstmt || difference) ;
EXECUTE IMMEDIATE sqlstmt || difference;
sqlstmt3 := 'SELECT ' || sequencename ||'.NEXTVAL from dual';
DBMS_OUTPUT.PUT_LINE('EXECUTE IMMEDIATE ' || sqlstmt3 || ' INTO sequenceValue') ;
EXECUTE IMMEDIATE sqlstmt3 INTO sequenceValue;
DBMS_OUTPUT.PUT_LINE('EXECUTE IMMEDIATE ' || sqlstmt || 1) ;
EXECUTE IMMEDIATE sqlstmt || 1;
DBMS_OUTPUT.PUT_LINE('') ;
END IF;
END LOOP;
CLOSE allseq;
END;
In some cases, you may find it easier to simply get the current max value and then
drop sequence x;
create sequence x start with {current max + 1};
The app will be broken after you do the drop. But that will keep anybody from inserting rows during that period, and creating a sequence is quick. Make sure you recreate any grants on the sequence since those will be dropped when the sequence is. And you may want to manually recompile any plsql that depends on the sequence.
Adding up to https://mcmap.net/q/614264/-oracle-9-resetting-sequence-to-match-the-state-of-the-table, but without resorting to SEQUENCENAME.NEXTVAL
hence not resulting in one position over it should be:
DECLARE
difference INTEGER;
alter_sequence_statement VARCHAR2 (255);
sequence_value NUMBER;
BEGIN
-- Base for the statement that will set the sequence value.
alter_sequence_statement :=
'ALTER SEQUENCE SEQUENCENAME INCREMENT BY ';
-- Fetch current last sequence value used.
SELECT
-- You could maybe want to make some further computations just
-- below if the sequence is using caching.
last_number
INTO sequence_value
FROM all_sequences
WHERE sequence_owner = 'SEQUENCEOWNER' AND sequence_name = 'SEQUENCENAME';
-- Compute the difference.
SELECT max(id) - sequence_value + 1 INTO difference
FROM SCHEMANAME.TABLENAME;
IF difference <> 0 THEN
-- Set the increment to a big offset that puts the sequence near
-- its proper value.
EXECUTE IMMEDIATE alter_sequence_statement || difference;
-- This 'sequence_value' will be ignored, on purpose.
SELECT SEQUENCENAME.NEXTVAL INTO sequence_value FROM dual;
-- Resume the normal pace of incrementing one by one.
EXECUTE IMMEDIATE alter_sequence_statement || 1;
END IF;
END;
Disclaimer: if the sequence is using caching (all_sequences.cache_size
set to bigger than 0) you are probably wanting to take it into consideration in the Compute the difference step.
SELECT setval( 'table_id_seq_name', (SELECT MAX(id) FROM table_name ) );
© 2022 - 2024 — McMap. All rights reserved.