Насколько плохо игнорирование исключения Oracle DUP_VAL_ON_INDEX? - PullRequest
10 голосов
/ 08 декабря 2008

У меня есть таблица, в которую я записываю, если пользователь просматривал объект хотя бы один раз, следовательно:

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

Оба поля НЕ являются НЕДЕЙСТВИТЕЛЬНЫМИ и вместе образуют первичный ключ.

Мой вопрос таков: мне все равно, сколько раз кто-то просматривал объект (после первого), у меня есть два варианта обработки вставок.

  • Выполните счетчик SELECT (*) ... и, если записи не найдены, вставьте новую запись.
  • Всегда просто вставляйте запись, и, если она генерирует исключения DUP_VAL_ON_INDEX (указывающие на то, что такая запись уже была), просто игнорируйте ее.

В чем недостаток выбора второго варианта?

UPDATE:

Полагаю, лучший способ выразить это так: «Являются ли издержки, вызванные исключением, хуже, чем издержки, вызванные начальным выбором?»

Ответы [ 5 ]

14 голосов
/ 09 декабря 2008

Обычно я просто вставляю и перехватываю исключение 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 в зависимости от относительной вероятности того, что вставки являются или не являются дубликатами.

1 голос
/ 09 декабря 2008

Попробуйте это?

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

Должно возвращаться 1, если оно есть, в противном случае NULL.

В вашем случае, это безопасно игнорировать, но для производительности следует избегать исключений на общем пути. Вопрос, который нужно задать: «Насколько распространенными будут исключения?» Достаточно мало, чтобы игнорировать? или так много другого метода следует использовать?

1 голос
/ 09 декабря 2008

Не думаю, что у вашего второго варианта есть обратная сторона. Я думаю, что это вполне допустимое использование именованного исключения, плюс оно позволяет избежать затрат на поиск.

0 голосов
/ 05 декабря 2012

ИМХО, лучше использовать вариант 2: Кроме того, что уже было сказано, вы должны учитывать безопасность потока . Если вы выберете опцию 1 и если ваш PL / SQL-блок выполняет несколько потоков, то возможно, что два или более потоков сработают одновременно, и в это время записи не будет, это приведет к тому, что все потоки будут вставлены и Вы получите уникальную ошибку ограничения.

0 голосов
/ 09 декабря 2008

Обычно обработка исключений происходит медленнее; однако, если это случится редко, вы избежите накладных расходов на запрос.
Я думаю, что это в основном зависит от частоты исключения, но если важна производительность, я бы предложил провести сравнительный анализ с обоими подходами.

Вообще говоря, рассматривать общие события как исключение - это неприятный запах; по этой причине вы можете видеть и с другой точки зрения.
Если это исключение, то оно должно рассматриваться как исключение - и ваш подход правильный.
Если это обычное событие, вы должны попытаться явно обработать его, а затем проверить, вставлена ​​ли уже запись.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...