¿Cómo podría secuencia.nextval ser nulo en Oracle?


11

Tengo una secuencia de Oracle definida así:

CREATE SEQUENCE  "DALLAS"."X_SEQ"  
    MINVALUE 0 
    MAXVALUE 999999999999999999999999999 
    INCREMENT BY 1 START WITH 0 NOCACHE  NOORDER  NOCYCLE ;

Se utiliza en un procedimiento almacenado para insertar un registro:

PROCEDURE Insert_Record
                (p_name    IN  VARCHAR2,                
                 p_userid  IN  INTEGER,
                 cur_out   OUT TYPES_PKG.RefCursor)
    IS
        v_id NUMBER := 0;
    BEGIN
        -- Get id value from sequence
        SELECT x_seq.nextval
          INTO v_id
          FROM dual;

        -- Line below is X_PKG line 40
        INSERT INTO X
            (the_id,            
             name,                        
             update_userid)
          VALUES
            (v_id,
             p_name,                        
             p_userid);

        -- Return new id
        OPEN cur_out FOR
            SELECT v_id the_id
              FROM dual;
    END;

Ocasionalmente, este procedimiento devuelve un error cuando se ejecuta desde el código de la aplicación.

ORA-01400: cannot insert NULL into ("DALLAS"."X"."THE_ID") 
ORA-06512: at "DALLAS.X_PKG", line 40 
ORA-06512: at line 1

Detalles que pueden o no ser relevantes:

  • Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Producción de 64 bits
  • El procedimiento se ejecuta a través de Microsoft.Practices.EnterpriseLibrary - Data.Oracle.OracleDatabase.ExecuteReader (comando DbCommand)
  • La aplicación no ajusta la llamada en una transacción explícita.
  • El inserto falla intermitentemente - menos del 1%

¿En qué circunstancias podría x_seq.nextvalser nulo?


¿Cuánto código hay entre seleccionar e insertar? ¿Hay algún bloque BEGIN..END o alguna declaración de EXCEPCIÓN en ese código? ¿Se hace referencia a v_id en ese código? Parece un poco extraño ¿Puede poner un bloque "SI v_id ES NULO ENTONCES ... FINALIZAR SI" directamente después de la instrucción y dejar algún resultado de depuración en algún lugar si la secuencia de hecho asigna nulo a v_id? Eso o envolver la secuencia de selección en un bloque COMENZAR ... EXCEPCIÓN, ya que podría estar sucediendo algo que no se ha detectado. Una última cosa: ¿hay un desencadenante en la tabla en la que está insertando que podría estar causándolo?
Philᵀᴹ

@Phil: la selección se realiza inmediatamente antes de la inserción. No BEGIN, END o EXCEPTION que no sea el proceso BEGIN / END. v_idsolo se hace referencia en la selección de secuencia, la inserción y el cursor final. Nuestro siguiente paso fue agregar el código de depuración. Es posible que tengamos que esperar los resultados, ya que solo ocurre en la producción y con muy poca frecuencia. Hay un disparador que se inserta en una tabla de auditoría. Lo he peinado sin una pistola humeante. El problema también ocurre ocasionalmente en otras tablas sin desencadenantes. Gracias por echar un vistazo.
Corbin marzo

55
Lo único que realmente puedo pensar en este momento es si: new.the_id de alguna manera se convertiría en NULL en el activador que está en la mesa X.
Phil

@ Phil: esta es sin duda la causa del problema. Deberías darle una respuesta.
René Nyffenegger

@ RenéNyffenegger: el problema también se produce en procesos que se insertan en tablas sin desencadenantes. Parece ser un error de igualdad de oportunidades.
Corbin marzo

Respuestas:


4

Estoy bastante seguro de que esto terminará siendo un artefacto de su código, o el controlador .net que está utilizando. He creado una demostración rápida para usted usando SQL puro - PL / SQL y nunca obtengo un valor de secuencia perdido. Por cierto, el cursor de referencia que está utilizando es probablemente innecesario y probablemente afecte el rendimiento y la legibilidad del código: mi demostración incluye un procedimiento insert_record2 que realiza constantemente más del 10% más rápido, en aproximadamente 26 segundos en mi computadora portátil frente a 36 para la versión del cursor de referencia. Al menos también creo que es más fácil de entender. Obviamente, podría ejecutar una versión modificada en su base de datos de prueba completa con el disparador de auditoría.

/* 
demo for dbse 
assumes a user with create table, create sequence, create procedure pivs and quota. 

*/

drop table dbse13142 purge;

create table dbse13142(
    the_id number not null
,   name   varchar2(20)
,   userid number)
;

drop sequence x_seq;
CREATE SEQUENCE  X_SEQ NOCACHE  NOORDER  NOCYCLE ;

create or replace PROCEDURE Insert_Record
                (p_name    IN  VARCHAR2,                
                 p_userid  IN  INTEGER,
                 cur_out   OUT sys_refcursor)
    IS
        v_id NUMBER := 0;
    BEGIN
        -- Get id value from sequence
        SELECT x_seq.nextval
          INTO v_id
          FROM dual;

        -- Line below is X_PKG line 40
        INSERT INTO dbse13142
            (the_id,            
             name,                        
             userid)
          VALUES
            (v_id,
             p_name,                        
             p_userid);

        -- Return new id
        OPEN cur_out FOR
            SELECT v_id the_id
              FROM dual;
    END;
/


create or replace PROCEDURE Insert_Record2
                (p_name    IN  VARCHAR2,                
                 p_userid  IN  INTEGER,
                 p_theid   OUT dbse13142.the_id%type)
    IS
    BEGIN
        -- Get id value from sequence
        SELECT x_seq.nextval
          INTO p_theid
          FROM dual;

        -- Line below is X_PKG line 40
        INSERT INTO dbse13142
            (the_id,            
             name,                        
             userid)
          VALUES
            (p_theid,
             p_name,                        
             p_userid);
    END;
/

set timing on

declare
   c sys_refcursor;
begin   
for i in 1..100000 loop
   insert_record('User '||i,i,c);
   close c;
end loop;
commit;
end;
/

select count(*) from dbse13142;
truncate table dbse13142;

declare
  x number;
begin   
for i in 1..100000 loop
   insert_record2('User '||i,i,x);
end loop;
commit;
end;
/

select count(*) from dbse13142;
truncate table dbse13142;

1
por cierto, una versión con el enfoque tradicional de usar un disparador para la columna the_id y el procedimiento siguiente también se ejecutó más rápido crear o reemplazar PROCEDURE Insert_Record3 (p_name IN dbse13142.name% type, p_userid IN dbse13142.userid% type, p_theid OUT dbse13142 .the_id% type) SE EMPIEZA A INSERTAR EN dbse13142 (name, userid) VALUES (p_name, p_userid) devolviendo the_id a p_theid; FIN; /
Niall Litchfield

Convino en que probablemente sea un problema con el código de la aplicación o el controlador. Tengo curiosidad por saber qué podría causar un nextval nulo como efecto secundario. Misterioso. Gracias por el consejo de rendimiento. Es un buen consejo que sugeriré al equipo.
Corbin marzo

1
Corbin, lo que quiero decir (y Kevin) es que algo extraño está sucediendo entre su código y Oracle: si ejecuta la prueba puramente en SQL, no obtendrá el efecto. Pero vea el comentario de Phil sobre el disparador de auditoría (que podría intentar deshabilitar).
Niall Litchfield

Entiendo los puntos hechos. El problema existe en los procesos que se insertan en tablas con y sin activadores, por lo que no se requiere un activador. Cuando existe un disparador, simplemente se inserta en una tabla de auditoría. He confirmado que no ha :new.the_idsido tocado. Entiendo que mi pregunta es una posibilidad remota. Es resistente a mi google-fu y tiene a varias personas rascándose la cabeza aquí. Me imaginé que alguien podría reconocer el síntoma (y el tratamiento) dado suficientes globos oculares. Gracias por echar un vistazo.
Corbin marzo

2

Intenta hacer un caso de prueba. Haga una tabla ficticia e inserte 100,000 registros usando su secuencia de la base de datos. Apuesto a que no tendrás problemas. Luego intente insertar lo mismo desde su aplicación.

¿Podría esto ser causado por otros problemas, como una falta de coincidencia del cliente Oracle?

Otra solución que solucionaría el problema pero no el problema es agregar un activador en la tabla.
Antes de Insertar en la tabla en Dallas.X IF: the_id es nulo ENTONCES SELECCIONE x_seq.nextval INTO: the_id FROM dual; TERMINARA SI;


No puedo recrear el problema localmente. Solo ocurre en la producción y solo allí con poca frecuencia. Mi presentimiento es que tienes razón sobre el cliente de Oracle. El problema apareció hace un par de semanas durante un lanzamiento en el que el cliente no se actualizó. Sin embargo, parece que algo no se lleva bien entre la aplicación y db. Las interacciones de otros consumidores parecen funcionar bien. La verificación nula no es una mala idea, pero idealmente me gustaría llegar a la raíz del problema en lugar de solucionarlo. ¿Quién sabe sin embargo? Una solución alternativa es mejor que rota.
Corbin marzo

0

Todavía no tengo privilegios para hacer comentarios, así que escribe esto como respuesta: dado que estás utilizando la versión de Oracle> = 11.1, que permite secuencias en expresiones PL / SQL en lugar de en SQL, prueba esto:

   v_id := x_seq.nextval;

En lugar de esto:

 -- Get id value from sequence
    SELECT x_seq.nextval
      INTO v_id
      FROM dual;

O bien, aunque he escuchado dudas / dificultades al usar ".currval", ¿tal vez omita la asignación separada de v_id y solo use este código ?:

 -- Line below is X_PKG line 40
        INSERT INTO X
            (the_id,            
             name,                        
             update_userid)
          VALUES
            (x_seq.nextval,
             p_name,                        
             p_userid);

        -- Return new id
        OPEN cur_out FOR
            SELECT x_seq.currval the_id
              FROM dual;

Lo siento, no tengo una instancia de 11g a mano ahora para probar esto.


Definitivamente no hay diferencia. Yo uso select into...en 11 tanto como en 9i y 10g. El único beneficio de 11+ es poder referenciarlo explícitamente como lo has señalado.
Ben
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.