2008-12-08 11 views
9

Tengo una tabla donde estoy grabando si un usuario ha visto un objeto al menos una vez, por lo tanto:¿Qué tan grave es ignorar la excepción Oracle DUP_VAL_ON_INDEX?

HasViewed 
    ObjectID number (FK to Object table) 
    UserId number (FK to Users table) 

Ambos campos no son NULL y juntos forman la clave principal.

Mi pregunta es, dado que no me importa cuántas veces alguien haya visto un objeto (después del primero), tengo dos opciones para manejar las inserciones.

  • Realice un recuento de SELECT (*) ... y, si no se encuentran registros, inserte un nuevo registro.
  • Inserte siempre un registro, y si arroja un DUP_VAL_ON_INDEX excepciones (lo que indica que ya existía ese registro), simplemente ignórelo.

¿Cuál es la desventaja de elegir la segunda opción?

ACTUALIZACIÓN:

Creo que la mejor manera de decirlo es: "¿Es la sobrecarga causada por la excepción peor que la sobrecarga causada por el selecto inicial"

Respuesta

13

Normalmente solo insertaría y atraparía la excepción DUP_VAL_ON_INDEX, ya que es el más simple de codificar. Esto es más eficiente que verificar la existencia antes de insertar. No considero que esto sea un "mal olor" (¡horrible frase!) Porque la excepción que manejamos la plantea Oracle, no es como plantear sus propias excepciones como un mecanismo de control de flujo.

Gracias al comentario de Igor ahora he ejecutado dos benchamrks diferentes sobre esto: (1) donde todos los intentos de inserción excepto el primero son duplicados, (2) donde todos los insertos no son duplicados. La realidad se ubicará en algún lugar entre los dos casos.

Nota: pruebas realizadas en Oracle 10.2.0.3.0.

Caso 1: Mayormente duplica

Parece que el enfoque más eficaz (en un factor significativo) es comprobar la existencia mientras se inserta:

prompt 1) Check DUP_VAL_ON_INDEX 
begin 
    for i in 1..1000 loop 
     begin 
     insert into hasviewed values(7782,20); 
     exception 
     when dup_val_on_index then 
      null; 
     end; 
    end loop 
    rollback; 
end; 
/

prompt 2) Test if row exists before inserting 
declare 
    dummy integer; 
begin 
    for i in 1..1000 loop 
     select count(*) into dummy 
     from hasviewed 
     where objectid=7782 and userid=20; 
     if dummy = 0 then 
     insert into hasviewed values(7782,20); 
     end if; 
    end loop; 
    rollback; 
end; 
/

prompt 3) Test if row exists while inserting 
begin 
    for i in 1..1000 loop 
     insert into hasviewed 
     select 7782,20 from dual 
     where not exists (select null 
         from hasviewed 
         where objectid=7782 and userid=20); 
    end loop; 
    rollback; 
end; 
/

Resultados (después de ejecutar una vez para evitar los gastos generales de análisis):

1) Check DUP_VAL_ON_INDEX 

PL/SQL procedure successfully completed. 

Elapsed: 00:00:00.54 
2) Test if row exists before inserting 

PL/SQL procedure successfully completed. 

Elapsed: 00:00:00.59 
3) Test if row exists while inserting 

PL/SQL procedure successfully completed. 

Elapsed: 00:00:00.20 

caso 2: no hay duplicados

prompt 1) Check DUP_VAL_ON_INDEX 
begin 
    for i in 1..1000 loop 
     begin 
     insert into hasviewed values(7782,i); 
     exception 
     when dup_val_on_index then 
      null; 
     end; 
    end loop 
    rollback; 
end; 
/

prompt 2) Test if row exists before inserting 
declare 
    dummy integer; 
begin 
    for i in 1..1000 loop 
     select count(*) into dummy 
     from hasviewed 
     where objectid=7782 and userid=i; 
     if dummy = 0 then 
     insert into hasviewed values(7782,i); 
     end if; 
    end loop; 
    rollback; 
end; 
/

prompt 3) Test if row exists while inserting 
begin 
    for i in 1..1000 loop 
     insert into hasviewed 
     select 7782,i from dual 
     where not exists (select null 
         from hasviewed 
         where objectid=7782 and userid=i); 
    end loop; 
    rollback; 
end; 
/

Resultados:

1) Check DUP_VAL_ON_INDEX 

PL/SQL procedure successfully completed. 

Elapsed: 00:00:00.15 
2) Test if row exists before inserting 

PL/SQL procedure successfully completed. 

Elapsed: 00:00:00.76 
3) Test if row exists while inserting 

PL/SQL procedure successfully completed. 

Elapsed: 00:00:00.71 

En este caso DUP_VAL_ON_INDEX gana por una milla. Tenga en cuenta que "seleccionar antes de insertar" es el más lento en ambos casos.

Parece que debe elegir la opción 1 o 3 de acuerdo con la probabilidad relativa de que las inserciones sean o no duplicadas.

+0

Asegúrate de ejecutar los puntos de referencia de Tony en tu entorno. Sé que tuvimos algunos problemas en una base de datos 10.2.0.2 o 10.2.0.3 en AIX, donde la ruta de excepción se volvió drásticamente más lenta: el código que funcionaba bien en 9.2 se ralentizaba a paso de tortuga. Había un parche para resolver el problema, pero fue una molestia. –

+0

La prueba es donde hay duplicados. ¿Cómo es el rendimiento cuando la nueva fila no es un duplicado (es decir, el que tiene el cheque explícito sigue haciendo el control, pero el manejador de excepciones no necesita ingresar). –

1

No creo que haya un inconveniente en su segunda opción. Creo que es un uso perfectamente válido de la excepción nombrada, además evita la sobrecarga de búsqueda.

1

¿Intenta esto?

SELECT 1 
FROM TABLE 
WHERE OBJECTID = 'PRON_172.JPG' AND 
     USERID='JCURRAN' 

Debería devolver 1, si hay uno allí, de lo contrario NULO.

En su caso, parece seguro ignorarlo, pero para el rendimiento, se deben evitar las excepciones en la ruta común. Una pregunta para preguntar: "¿Cuán comunes serán las excepciones?" ¿Pocos para ignorar? o tantos otros deberían ser usados?

+0

Esto aumentará la excepción NO_DATA_FOUND si no hay fila, no devolverá NULL. –

+0

Sí. Estoy pensando en sql, y es una pregunta pl/sql. – EvilTeach

0

Normalmente, el manejo de excepciones es más lento; sin embargo, si solo ocurre pocas veces, entonces evitaría la sobrecarga de la consulta.
Creo que depende principalmente de la frecuencia de la excepción, pero si el rendimiento es importante, sugeriría una evaluación comparativa con ambos enfoques.

En términos generales, el tratamiento de eventos comunes como excepción es un mal olor; por esta razón, puedes ver también desde otro punto de vista.
Si se trata de una excepción, debe tratarse como una excepción, y su enfoque es el correcto.
Si se trata de un evento común, entonces debe tratar de manejarlo explícitamente y luego verificar si el registro ya está insertado.

+0

La captura de la excepción DUP_VAL_ON_INDEX es más rápida que la comprobación de existencia, como demostraré en mi respuesta. En cuanto al "mal olor", no veo nada malo en atrapar una excepción planteada por Oracle y manejarla de manera apropiada; esto es diferente a plantear su PROPIA excepción para que no haya errores. –

+0

No estoy de acuerdo. El mismo nombre "excepción" nos dice que las excepciones no se deben usar para el flujo normal del programa, sin importar su origen. De lo contrario, es un mal olor. Además, su respuesta demuestra que prevenir la excepción es la forma más rápida (consulte los resultados para "Probar si existe una fila al insertar"). –

+1

No, no, ¡demuestra exactamente lo contrario! ¡Es absurdo no usar la mejor herramienta para el trabajo simplemente porque no le gusta el "olor" de su nombre! –

0

En mi humilde opinión, es mejor ir con la Opción 2: Aparte de lo que ya se ha dicho, debe considerar hilo de seguridad. Si opta por la opción 1 y Si varios hilos están ejecutando su bloque PL/SQL, entonces es posible que dos o más hilos seleccionen fuego al mismo tiempo y en ese momento no hay registro, esto terminará liderando todos los hilos para insertar y obtendrá un error de restricción único.