Oracle 9 - Reimpostazione della sequenza in modo che corrisponda allo stato della tabella

StackOverflow https://stackoverflow.com/questions/1426647

  •  07-07-2019
  •  | 
  •  

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?

È stato utile?

Soluzione

Se ID è il nome della colonna PK e PK_SEQ è il nome della sequenza:

  1. Trova il valore del PK più alto di SELEZIONA MAX (ID) DA tableName

  2. Trova il valore del prossimo PK_SEQ di     SELEZIONA PK_SEQ.NEXTVAL DA DUAL

  3. Se # 2 > # 1 allora non c'è bisogno di essere     fatto, supponendo che li tratti     valori come chiavi surrogate vere
  4. Altrimenti, modifica la sequenza in     passa all'ID massimo di ALTER SEQUENCE     INCREMENTO PK_SEQ DI [valore # 1 - # 2     value]
  5. Salta la sequenza di SELEZIONA     PK_SEQ.NEXTVAL FROM DUAL

  6. 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 SEQUENCENAME.NEXTVAL quindi non comportare una posizione su di esso dovrebbe essere:

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.

Documentazione Oracle per tutte le sequenze ... .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top