Oracle 9 - Reimpostazione della sequenza in modo che corrisponda allo stato della tabella
Domanda
Ho una sequenza usata per seminare le mie chiavi primarie (basate su numeri interi) in una tabella di Oracle.
Sembra che questa sequenza non sia sempre stata utilizzata per inserire nuovi valori nella tabella. Come posso riportare la sequenza al passo con i valori effettivi nella tabella?
Soluzione
Se ID è il nome della colonna PK e PK_SEQ è il nome della sequenza:
-
Trova il valore del PK più alto di SELEZIONA MAX (ID) DA tableName
-
Trova il valore del prossimo PK_SEQ di SELEZIONA PK_SEQ.NEXTVAL DA DUAL
- Se # 2 > # 1 allora non c'è bisogno di essere fatto, supponendo che li tratti valori come chiavi surrogate vere
- Altrimenti, modifica la sequenza in passa all'ID massimo di ALTER SEQUENCE INCREMENTO PK_SEQ DI [valore # 1 - # 2 value]
-
Salta la sequenza di SELEZIONA PK_SEQ.NEXTVAL FROM DUAL
-
Reimposta il valore dell'incremento della sequenza a 1 di ALTER SEQUENCE PK_SEQ INCREMENTO DI 1
Tutto questo presuppone che non ci siano nuovi inserimenti nella tabella mentre lo stai facendo ...
Altri suggerimenti
In breve, gioco:
-- 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.
Puoi ottenere il massimo valore di sequenza usato nella tua tabella, fare i calcoli e aggiornare la sequenza di conseguenza.
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;
Ho creato questo script perché non ho trovato uno script online che imposta dinamicamente tutto le mie sequenze sull'ID più alto corrente. Testato su 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 alcuni casi, potresti trovare più semplice semplicemente ottenere il valore massimo corrente e quindi
drop sequence x;
create sequence x start with {current max + 1};
L'app verrà interrotta dopo aver effettuato il rilascio. Ma ciò impedirà a chiunque di inserire righe durante quel periodo e la creazione di una sequenza è rapida. Assicurati di ricreare eventuali concessioni sulla sequenza poiché verranno eliminate quando la sequenza è. E potresti voler ricompilare manualmente qualsiasi plsql che dipende dalla sequenza.
Aggiunta fino a https://stackoverflow.com/a/15929548/1737973 , ma senza ricorrere a
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;
Dichiarazione di non responsabilità: se la sequenza utilizza la memorizzazione nella cache ( all_sequences.cache_size
impostato su un valore maggiore di 0) probabilmente vorrai tenerne conto nel Calcola la differenza < em> passo.