Обычно я просто вставляю и перехватываю исключение DUP_VAL_ON_INDEX, так как его проще всего кодировать. Это более эффективно, чем проверка существования перед вставкой. Я не считаю это "дурным запахом" (ужасная фраза!), потому что обрабатываемое нами исключение вызывается Oracle - это не похоже на создание собственных исключений в качестве механизма управления потоком.
Благодаря комментарию Игоря я запустил два разных теста для этого: (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 в зависимости от относительной вероятности того, что вставки будут или не будут дубликатами.
person
Tony Andrews
schedule
09.12.2008