2011-05-23 50 views
29

Por alguna razón, las personas en el pasado han insertado datos sin usar sequence.NEXTVAL. Entonces cuando voy a usar sequence.NEXTVAL para llenar una tabla, recibo una violación de PK, ya que ese número ya está en uso en la tabla.¿La mejor manera de restablecer una secuencia de Oracle al siguiente valor en una columna existente?

¿Cómo puedo actualizar el siguiente valor para que se pueda utilizar? En este momento, solo estoy insertando una y otra vez hasta que sea exitoso (INSERT INTO tbl (pk) VALUES (sequence.NEXTVAL)), y eso sincroniza el nextval.

+0

@rationalSpring Terminé decidiendo dejar caer y recrear la secuencia. –

+0

Ver [esta pregunta similar] (http://stackoverflow.com/questions/6057191/sequence-creation-in-oracle/6057249#6057249) –

+1

El problema con el descarte y la recreación de la secuencia es que debe volver a aplicar cualquier otorga en él (sin mencionar que invalida temporalmente cualquier vista o PL/SQL que se refiera a ella). –

Respuesta

13

Estos dos procedimientos me permiten restablecer la secuencia y restablecer la secuencia en función de los datos una mesa (disculpas por las convenciones de codificación utilizados por este cliente):

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; 
+0

¿Funciona esto con 11g? Obtengo "Opción de configuración desconocida ~~~" – Worthy7

53

Puede aumentar temporalmente el tamaño de la caché y hacer un muñeco de selección y luego restablecer teh tamaño de la caché de nuevo a 1. Así, por ejemplo

ALTER SEQUENCE mysequence INCREMENT BY 100; 

select mysequence.nextval from dual; 

ALTER SEQUENCE mysequence INCREMENT BY 1; 
+4

Sneaky. Me gusta. –

+1

Es posible que desee asegurarse de que nadie más esté utilizando la secuencia mientras se modifica. –

9

Si usted puede contar con tener un período de tiempo donde la mesa es en un estado estable sin nuevos insertos pasando, esto debería hacerlo (no probado):

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; 

esto le permite obtener la secuencia de vuelta en sincronía con la mesa, sin que se caiga/recreando/re-concesión de la secuencia . Tampoco usa DDL, por lo que no se realizan confirmaciones implícitas. Por supuesto, tendrá que perseguir y abofetear a las personas que insisten en no usar la secuencia para completar la columna ...

+1

Esto funciona, solo necesita un "FIN SI" después de la línea IF – tomgeraghty3

9

con 10,2 g de Oracle:

select level, sequence.NEXTVAL 
from dual 
connect by level <= (select max(pk) from tbl); 

establecerá el valor de secuencia actual al máximo (pico) de la tabla (es decir, la próxima llamada a NEXTVAL le dará el resultado correcto); si usa Toad, presione F5 para ejecutar la instrucción, no F9, que pagina la salida (deteniendo así el incremento después de, generalmente, 500 filas). Buen lado: esta solución solo es DML, no DDL. Solo SQL y no PL-SQL. Lado malo: esta solución imprime las máximas (pk) filas de salida, es decir, suele ser más lenta que la solución ALTER SEQUENCE.

+0

No 'max (pk) + 1'? – Worthy7

+0

No: después de ejecutar la selección, el valor actual de la secuencia será max (pk), por lo tanto, cuando necesite el siguiente valor de la secuencia, la secuencia de llamada.NEXTVAL le dará el valor de max (pk) +1. De todos modos, pruébalo: es hermoso. – tech

4

En mi caso tengo una secuencia llamada PS_LOG_SEQ que tenía LAST_NUMBER = 3920.

Luego importé algunos datos de PROD a mi máquina local y los inserté en la tabla PS_LOG. Los datos de producción tenían más de 20000 filas con la última log_id (clave principal) siendo 20070. Después de importar traté de insertar nuevas filas en esta tabla, pero al guardar me dieron una excepción como ésta:

ORA-00001: unique constraint (LOG.PS_LOG_PK) violated 

Sin duda, esto tiene para hacer con la secuencia PS_LOG_SEQ asociada con la tabla PS_LOG. El LAST_NUMBER colisionaba con datos importados que ya habían utilizado el siguiente valor de ID del PS_LOG_SEQ.

Para resolver que he utilizado este comando para actualizar la secuencia de la última \ max(LOG_ID) + 1:

alter sequence PS_LOG_SEQ restart start with 20071; 

Este comando restablece el valor LAST_NUMBER y entonces podría insertar nuevas filas en la tabla. No más colisión.:)

Nota: este comando alter sequence es nuevo en Oracle 12c.

Cuestiones relacionadas