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.
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. –
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). –