Configuraciones idénticas de Oracle db: excepción en solo una de ellas

editar: Mire al final de esta pregunta para saber qué causó el error y cómo me enteré.

Tengo una excepción muy extraña lanzada sobre mí desde Hibernate cuando ejecuto una aplicación que inserta datos por lotes en una base de datos de Oracle. El error proviene de la base de datos de Oracle, ORA-00001, cual

"significa que se ha intentado insertar un registro con una clave duplicada (única). Este error también se generará si se actualiza un registro existente para generar una clave duplicada (única)".

El error es extraño porque he creado la misma tabla (exactamente la misma definición) en otra máquina donde NO obtengo el mismo error si lo uso a través de mi aplicación. Y todos los datos se insertan en la base de datos, por lo que en realidad no se rechaza nada.

Tiene que haber algo diferente entre las dos configuraciones, pero lo único que puedo ver que es diferente es la salida de banner que obtengo al emitir

select * from v$version where banner like 'Oracle%';

La base de datos que me da problemas: Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Prod
El que funciona: Oracle Database 10g Release 10.2.0.3.0 - 64bit Production

Las definiciones de tabla, la entrada y la aplicación que escribí son las mismas para ambos. La tabla involucrada es básicamente una tabla de cuatro columnas con una identificación compuesta (serviceid, fecha, valor1, valor2), nada lujoso.

¿Alguna idea sobre lo que puede estar mal? Comencé limpio varias veces, descartando ambas tablas para comenzar en igualdad de condiciones, pero todavía obtengo el error de la base de datos.

Algo más de la salida:

Caused by: java.sql.BatchUpdateException: ORA-00001: unique constraint (STATISTICS.PRIMARY_KEY_CONSTRAINT) violated

    at oracle.jdbc.driver.DatabaseError.throwBatchUpdateException(DatabaseError.java:367)
    at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:8728)
    at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)

Cómo descubrí qué causó el problema

Gracias a APC e ik_zelf, pude identificar la causa raíz de este error. Resulta que el programador Quartz se configuró incorrectamente para la base de datos de producción (donde apareció el error). Para el trabajo que se ejecuta contra el servidor Oracle que no falla, tenía <cronTriggerExpression>0/5 * * * * ?</cronTriggerExpression> que ejecutó el trabajo por lotes cada cinco segundos. Pensé que una vez por minuto era suficiente para el otro servidor de Oracle, y configuré el programador de cuarzo con * * / 1 * * *?. Esto resulta ser incorrecto, y en lugar de correr cada minuto, ¡esto corría cada segundo!

Cada trabajo tomó aproximadamente 1.5-2 segundos y, por lo tanto, dos o más trabajos se ejecutaron simultáneamente, lo que provocó inserciones simultáneas en el servidor. Entonces, en lugar de insertar 529 elementos, obtenía entre 1000 y 2000 inserciones. Cambiar la expresión crontrigger a la misma que la otra, que se ejecutaba cada cinco segundos, solucionó el problema.

Para averiguar qué estaba mal, tuve que establecer verdadero en hibernate.cfg.xml y deshabilitar la restricción de clave principal en la tabla.

-- To catch exceptions
-- to find the offending rows run the following query
-- SELECT * FROM uptime_statistics, EXCEPTIONS WHERE MY_TABLE.rowid = EXCEPTIONS.row_id;
create table exceptions(row_id rowid,
                        owner varchar2(30),
                        table_name varchar2(30),
                        constraint varchar2(30));

-- This table was set up
CREATE TABLE MY_TABLE
  (
    LOGDATE DATE NOT NULL,
    SERVICEID           VARCHAR2(255 CHAR) NOT NULL,
    PROP_A   NUMBER(10,0),
    PROP_B NUMBER(10,0),
    CONSTRAINT PK_CONSTRAINT PRIMARY KEY (LOGDATE, SERVICEID)
  );

-- Removed the constraint to see what was inserted twice or more
alter table my_table
  disable constraint PK_CONSTRAINT;

-- Enable this later on to find rows that offend the constraints
alter table my_table
  enable constraint PK_CONSTRAINT
    exceptions into exceptions;

preguntado el 16 de mayo de 11 a las 16:05

3 Respuestas

Tiene una restricción compuesta única. ORA-00001 significa que tiene dos o más filas que tienen valores duplicados en ServiceID, Date, Value1 y / o Value2. Dice que la entrada es la misma para ambas bases de datos. Así que tampoco:

  • estás imaginando que tu programa lanza ORA-00001
  • se equivoca al decir que la entrada es la misma en ambas ejecuciones.

La explicación más probable es la segunda: una o más de sus columnas clave está poblada por una fuente externa o un valor predeterminado (por ejemplo, tabla de códigos para ServiceId o SYSDATE para la columna de fecha). En su base de datos defectuosa, esta población automática no proporciona un valor único. Puede haber varias razones por las que esto podría ser así, dependiendo de qué mecanismo (s) esté utilizando. Recuerde que en una clave compuesta única las entradas NULL cuentan. Es decir, puede tener cualquier número de registros (NULL, NULL.NULL, NULL) pero solo uno para (42, NULL, NULL, NULL).

Es difícil para nosotros adivinar cuál podría ser el problema real, y casi tan difícil para ti (aunque tienes la ventaja de ser el autor del código, lo que debería darte una idea). Lo que necesita son algunas declaraciones de seguimiento. Mi solución preferida sería usar Manejo de excepciones de DML masivo pero luego soy un fan de PL / SQL. Hibernate le permite conectar algunos registros a sus programas: le sugiero que lo encienda. El código es mucho más fácil de depurar cuando tiene una instrumentación decente.

Como último recurso, desactive la restricción antes de ejecutar la inserción por lotes. Luego, vuelva a habilitarlo así:

alter table t42
    enable constraint t42_uk
        exceptions into my_exceptions
/

Esto fallará si tiene filas duplicadas, pero lo más importante es que la tabla MY_EXCEPTIONS enumerará todas las filas que entran en conflicto. Eso al menos le dará alguna pista sobre el origen de la duplicación. Si aún no tiene una tabla de excepciones, tendrá que ejecutar un script: $ORACLE_HOME/rdbms/admin/utlexcptn.sql (es posible que necesite un DBA para obtener acceso a este directorio).


tl; dr

insight requiere información: instrumente su código.

contestado el 17 de mayo de 11 a las 15:05

Gran consejo sobre excepciones. Intentaré eso e informaré sobre mis hallazgos. - oligofren

Al deshabilitar la restricción de clave principal, hacer las inserciones y luego volver a habilitarla, pude señalar que estaba haciendo muchas inserciones dos veces. Eso podría deberse a algunos cambios en el código que estaba haciendo al intentar averiguar qué estaba mal, ya que esto aún debería haber provocado un error al hacer inserciones en la otra base de datos, pero estoy mucho más cerca de descubrir qué estaba mal . El artículo de ik_zelf, Reporting Constraint Exceptions, también ayudó a identificar qué excepciones se lanzaron y dónde. - oligofren

Descubrí qué estaba causando los errores dobles. Sin embargo, sigo dándote la respuesta. Actualizando mi pregunta para incorporar la causa del error. - oligofren

El que tiene problemas es EE y el otro parece una base de datos SE. Espero que el primero sea en hardware más rápido. Si ese es el caso, y su columna de fecha se llena usando SYSDATE, es muy posible que la resolución de tiempo no sea suficiente; que obtiene valores de fecha duplicados. Si las otras columnas de sus datos tampoco son únicas, obtiene ORA-00001.

Es una posibilidad remota, pero a primera vista miraría en esta dirección.

¿Puede utilizar una tabla de excepciones para identificar los datos? Ver Informes de excepciones de restricciones

contestado el 16 de mayo de 11 a las 21:05

Buen consejo sobre excepciones. Sysdate es un callejón sin salida ya que genero las fechas del lado del cliente y son fechas, no fecha y hora. - oligofren

Mi conjetura sería la identificación del servicio. Cualquier service_id que hibernate esté usando para la inserción 'nueva' ya se ha usado.

Posiblemente la tabla esté vacía en una base de datos pero poblada en otra

Sin embargo, apuesto a que el service_id se genera en secuencia y el número de secuencia no está sincronizado con el contenido de los datos. Entonces tienes las mismas 1000 filas en la tabla pero haciendo

SELECT service_id_seq.nextval FROM DUAL

en una base de datos da un número menor que en la otra. Veo esto mucho donde se ha creado la secuencia (por ejemplo, fuera del control de la fuente) y se han importado datos a la tabla desde otra base de datos.

contestado el 17 de mayo de 11 a las 07:05

Todos los valores son insertados por la aplicación. Ninguno se genera automáticamente. El ID de servicio es solo una cadena y la fecha no tiene parte de tiempo. - oligofren

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.