Oracle 9 - Réinitialisation de la séquence pour correspondre à l'état de la table
Question
J'ai une séquence utilisée pour ensemencer mes clés primaires (basées sur un entier) dans une table oracle.
Il apparaît que cette séquence n'a pas toujours été utilisée pour insérer de nouvelles valeurs dans le tableau. Comment retrouver la séquence en phase avec les valeurs réelles du tableau?
La solution
Si ID est le nom de votre colonne de PK et si PK_SEQ est le nom de votre séquence:
-
Trouvez la valeur du PK le plus élevé en SELECT MAX (ID) FROM nomTable
-
Trouvez la valeur du prochain PK_SEQ en SÉLECTIONNEZ PK_SEQ.NEXTVAL FROM DUAL
- Si n ° 2 > # 1 alors rien ne doit être fait, en supposant que vous traitez ces des valeurs en tant que vraies clés de substitution
- Sinon, modifiez la séquence en passer au max ID par ALTER SEQUENCE PK_SEQ INCREMENT BY [# 1 valeur - # 2 valeur]
-
Bump la séquence par SELECT PK_SEQ.NEXTVAL FROM DUAL
-
Réinitialiser la valeur d'incrément de séquence à 1 par ALTER SEQUENCE PK_SEQ INCRÉMENT DE 1
Tout cela suppose que vous n'ayez pas de nouvelles insertions dans la table pendant que vous le faites ...
Autres conseils
En bref, jouez:
-- 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.
Vous pouvez obtenir la valeur de séquence maximale utilisée dans votre tableau, faire le calcul et mettre à jour la séquence en conséquence.
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;
J'ai créé ce script car je n'ai pas trouvé de script en ligne qui définisse de manière dynamique toutes mes séquences sur l'ID actuel le plus élevé. Testé sur 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;
Dans certains cas, il vous sera peut-être plus facile d'obtenir simplement la valeur maximale actuelle, puis
.drop sequence x;
create sequence x start with {current max + 1};
L'application sera brisée après que vous ayez effectué le largage. Mais cela empêchera quiconque d'insérer des lignes pendant cette période et la création d'une séquence est rapide. Assurez-vous de recréer toutes les attributions de la séquence car celles-ci seront supprimées lors de la séquence. Et vous voudrez peut-être recompiler manuellement les fichiers plsql qui dépendent de la séquence.
Ajout jusqu’à https://stackoverflow.com/a/15929548/1737973 , mais sans recourir à SEQUENCENAME.NEXTVAL
ne doit donc pas donner lieu à une position unique:
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;
Clause de non-responsabilité: si la séquence utilise la mise en cache ( all_sequences.cache_size
définie sur une valeur supérieure à 0), vous souhaiterez probablement en tenir compte dans la Calculer la différence < em> step.