Обикновено просто бих вмъкнал и прихванал изключението DUP_VAL_ON_INDEX, тъй като това е най-лесното за кодиране. Това е по-ефективно от проверката за съществуване преди вмъкване. Не считам това за „лоша миризма“ (ужасна фраза!), защото изключението, което обработваме, се генерира от Oracle – не е като да създавате свои собствени изключения като механизъм за контрол на потока.
Благодарение на коментара на Игор, сега изпълних два различни benchamrks за това:(1) където всички опити за вмъкване с изключение на първия са дубликати, (2) където всички вмъквания не са дубликати. Реалността ще се намира някъде между двата случая.
Забележка:тестовете са извършени на Oracle 10.2.0.3.0.
Случай 1:Предимно дубликати
Изглежда, че най-ефективният подход (по значим фактор) е да се провери за съществуване ДОКАТО вмъквате:
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;
/
Резултати (след еднократно изпълнение, за да се избегнат допълнителни разходи за анализ):
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
Случай 2:без дубликати
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;
/
Резултати:
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
В този случай DUP_VAL_ON_INDEX печели с една миля. Забележете, че „избиране преди вмъкване“ е най-бавното и в двата случая.
Така че изглежда, че трябва да изберете опция 1 или 3 според относителната вероятност вложките да бъдат или да не са дубликати.