Обычно часто фиксировать получается отнюдь не быстрее — почти всегда быстрее все сделать единственным оператором SQL. Для небольшого примера представим, что у нас есть таблица T с множеством строк, и мы хотим обновить значение столбца для каждой строки в этой таблице. Для демонстрации используются две таблицы — T1 и T2:
ops$tkyte@ORA10G> create table t1 as select * from all_objects;
Table created.
Таблица создана.
ops$tkyte@ORA10G> exec dbms_stats.gather_table_stats( user, 'T1' );
PL/SQL procedure successfully completed.
Процедура PL/SQL успешно завершена.
ops$tkyte@ORA10G> create table t2 as select * from t1;
Table created.
Таблица создана.
ops$tkyte@ORA10G> exec dbms_stats.gather_table_stats( user, 'T2' );
PL/SQL procedure successfully completed.
Процедура PL/SQL успешно завершена.
Приступая к обновлению, мы можем просто выполнить его единственным оператором UPDATE, как здесь:
ops$tkyte@ORA10G> set timing on
ops$tkyte@ORA10G> update t1 set object_name = lower(object_name);
48306 rows updated.
48306 строк обновлено.
Elapsed: 00:00:00.31
Общее время: 00:00:00.31
Многие люди по разным причинам предпочитают делать это так, как показано ниже:
ops$tkyte@ORA10G> begin
2 for x in ( select rowid rid, object_name, rownum r
3 from t2 )
4 loop
5 update t2
6 set object_name = lower(x.object_name)
7 where rowid = x.rid;
8 if ( mod(x.r,100) = 0 ) then
9 commit;
10 end if;
11 end loop;
12 commit;
13 end;
14 /
PL/SQL procedure successfully completed.
Процедура PL/SQL успешно завершена.
Elapsed: 00:00:05.38
Общее время: 00:00:05.38
На этом простом примере видно, что многократная фиксация в цикле в несколько раз замедляет операцию. Если вы можете сделать что-то единственным оператором SQL, так и делайте — это почти наверняка будет быстрее. Даже если “оптимизировать” код процедуры, используя групповую обработку обновлений, как показано ниже:
ops$tkyte@ORA10G> declare
9 begin
10 open c; 11 loop12 fetch c bulk collect into l_rids, l_names LIMIT 100;13 forall i in 1 .. l_rids.count 14 update t2 15 set object_name = lower(l_names(i))16 where rowid = l_rids(i);17 commit;18 exit when c%notfound;19 end loop; 20 close c; 21 end;22 /
PL/SQL procedure successfully completed.
Процедура PL/SQL успешно завершена.
Elapsed: 00:00:02.36
Общее время: 00:00:02.36
будет действительно намного быстрее, но все же много медленнее, чем могло бы быть. И, кроме того, вы должны заметить, что код становится все более и более сложным. От предельной простоты единственного оператора UPDATE к процедурному коду, затем к еще более сложному процедурному коду — мы явно движемся в неверном направлении!
Теперь, просто чтобы поддержать контрапункт этой дискуссии, вспомним главу 7, где мы говорили о концепции согласованной записи и о том, как оператор UPDATE, например, может автоматически перезапускаться. В случае, когда процедурный оператор UPDATE должен работать с подмножеством записей (имея конструкцию WHERE), а другие пользователи модифицируют столбцы, которые этот оператор UPDATE использует в своей конструкции WHERE, тогда имеет смысл либо использовать серии мелких транзакций вместо одной большой, или же блокировать таблицу перед тем, как выполнять массовое обновление. Целью этого является снижение вероятности перезапусков. Если мы собираемся обновить UPDATE значительную часть строк таблицы, это приводит к необходимости применения команды LOCK TABLE. Согласно моему опыту, однако, подобного рода массовые обновления или массовые удаления (только этого типа операторы могут быть субъектами перезапуска) выполняются в изоляции. Такие крупные единовременные пакетные
обновления или удаления старых данных обычно не выполняются в период высокой активности. В самом деле, удаление данных вообще не должно быть затронуто этим, поскольку вы обычно работаете с некоторыми полями типа даты, чтобы найти старую информацию, подлежащую удалению, а другие приложения обычно эти даты не модифицируют.
< Предыдущая | Следующая > |
---|