Pour une raison quelconque, des personnes dans le passé ont inséré des données sans utiliser sequence.NEXTVAL. Ainsi, lorsque je vais utiliser sequence.NEXTVAL afin de remplir une table, j'obtiens une violation de PK, car ce numéro est déjà utilisé dans la table.
Comment mettre à jour la prochaine valeur pour qu'elle soit utilisable? En ce moment, j'insère encore et encore jusqu'à ce qu'il réussisse (INSERT INTO tbl (pk) VALUES (sequence.NEXTVAL)
), et cela synchronise la valeur suivante.
Ces deux procédures me permettent de réinitialiser la séquence et de réinitialiser la séquence en fonction des données d'une table (excuses pour les conventions de codage utilisées par ce client):
CREATE OR REPLACE PROCEDURE SET_SEQ_TO(p_name IN VARCHAR2, p_val IN NUMBER)
AS
l_num NUMBER;
BEGIN
EXECUTE IMMEDIATE 'select ' || p_name || '.nextval from dual' INTO l_num;
-- Added check for 0 to avoid "ORA-04002: INCREMENT must be a non-zero integer"
IF (p_val - l_num - 1) != 0
THEN
EXECUTE IMMEDIATE 'alter sequence ' || p_name || ' increment by ' || (p_val - l_num - 1) || ' minvalue 0';
END IF;
EXECUTE IMMEDIATE 'select ' || p_name || '.nextval from dual' INTO l_num;
EXECUTE IMMEDIATE 'alter sequence ' || p_name || ' increment by 1 ';
DBMS_OUTPUT.put_line('Sequence ' || p_name || ' is now at ' || p_val);
END;
CREATE OR REPLACE PROCEDURE SET_SEQ_TO_DATA(seq_name IN VARCHAR2, table_name IN VARCHAR2, col_name IN VARCHAR2)
AS
nextnum NUMBER;
BEGIN
EXECUTE IMMEDIATE 'SELECT MAX(' || col_name || ') + 1 AS n FROM ' || table_name INTO nextnum;
SET_SEQ_TO(seq_name, nextnum);
END;
Vous pouvez augmenter temporairement la taille du cache et effectuer une sélection fictive, puis réinitialiser la taille du cache à 1. Ainsi, par exemple
ALTER SEQUENCE mysequence INCREMENT BY 100;
select mysequence.nextval from dual;
ALTER SEQUENCE mysequence INCREMENT BY 1;
Si vous pouvez compter sur une période de temps où la table est dans un état stable sans nouvelle insertion, cela devrait le faire (non testé):
DECLARE
last_used NUMBER;
curr_seq NUMBER;
BEGIN
SELECT MAX(pk_val) INTO last_used FROM your_table;
LOOP
SELECT your_seq.NEXTVAL INTO curr_seq FROM dual;
IF curr_seq >= last_used THEN EXIT;
END IF;
END LOOP;
END;
Cela vous permet de synchroniser la séquence avec la table, sans supprimer/recréer/ré-accorder la séquence. Il n'utilise pas non plus de DDL, donc aucune validation implicite n'est effectuée. Bien sûr, vous allez devoir traquer et gifler les gens qui insistent pour ne pas utiliser la séquence pour remplir la colonne ...
Dans mon cas, j'ai une séquence appelée PS_LOG_SEQ
Qui avait un LAST_NUMBER = 3920
.
J'ai ensuite importé des données de PROD
sur ma machine locale et inséré dans la table PS_LOG
. Les données de production avaient plus de 20000
Lignes, la dernière LOG_ID (clé primaire) étant 20070. Après l'importation, j'ai essayé d'insérer de nouvelles lignes dans ce tableau mais lors de l'enregistrement, j'ai eu une exception comme celle-ci:
ORA-00001: unique constraint (LOG.PS_LOG_PK) violated
Cela a sûrement à voir avec la séquence PS_LOG_SEQ
Associée à la table PS_LOG
. Le LAST_NUMBER
Entrait en collision avec des données que j'avais importées qui avaient déjà utilisé la prochaine valeur d'ID du PS_LOG_SEQ
.
Pour résoudre ce problème, j'ai utilisé cette commande pour mettre à jour la séquence vers la dernière\max(LOG_ID)
+ 1:
alter sequence PS_LOG_SEQ restart start with 20071;
Cette commande a réinitialisé la valeur LAST_NUMBER
Et j'ai pu ensuite insérer de nouvelles lignes dans le tableau. Plus de collision. :)
Remarque: cette commande alter sequence
est nouvelle dans Oracle 12c.
Avec Oracle 10.2g:
select level, sequence.NEXTVAL
from dual
connect by level <= (select max(pk) from tbl);
définira la valeur de séquence actuelle au maximum (pk) de votre table (c'est-à-dire que le prochain appel à NEXTVAL vous donnera le bon résultat); si vous utilisez Toad, appuyez sur F5 pour exécuter l'instruction, et non F9, qui affiche la sortie (arrêtant ainsi l'incrément après, généralement, 500 lignes). Bon côté: cette solution est uniquement DML, pas DDL. Uniquement SQL et pas PL-SQL. Mauvais côté: cette solution imprime max (pk) lignes de sortie, c'est-à-dire est généralement plus lente que la solution ALTER SEQUENCE.