Да, действительно, обработка исключений достаточно медленна, однако, не стоит сразу же пытаться избегать их везде, где только можно, да еще и любыми способами. Например, часто вижу как их пытаются избегать даже в случаях поиска по первичному ключу c минимальной вероятностью получения no_data_found.
В целом, стоит учитывать вероятную частоту возникновения исключений и overhead, добавляемый выбранным способом обхода исключений.
Поясню на примере, о котором говорил: пусть есть код, который возвращает поле из таблицы по pk и в случае, если такой записи нет, возвращает null.
create table t_test(a primary key, b) as select level,level from dual connect by level<=1e5;
create or replace function f1(p in number) return number as res number; begin select/*+ F1 */ b into res from t_test t where t.a=p; return res; exception when no_data_found then return null; end;Как я вижу, наиболее часто в таких случаях пытаются обойти механизм исключений следующими способами:
create or replace function f2(p in number) return number as begin for rec in (select/*+ F2 */ b from t_test t where t.a=p) loop return rec.b; end loop; return null; end;
create or replace function f2(p in number) return number as res number; begin for rec in (select/*+ F2 */ b from t_test t where t.a=p) loop res:=rec.b; end loop; return res; end;
create or replace function f3(p in number) return number as res number; begin select/*+ F3 */ min(b) into res from t_test t where t.a=p; return res; end;
create or replace function f4(p in number) return number as res number; begin select/*+ F4 */ (select b from t_test t where t.a=p) into res from dual; return res; end;
declare v integer; v_start integer:= 1; v_end integer:= 100000; l_timer integer := dbms_utility.get_time; procedure print(msg varchar2) is begin dbms_output.put_line(to_char((dbms_utility.get_time-l_timer)/100,'9990.00')||' '||msg); l_timer:=dbms_utility.get_time; end; begin print('start'); for i in v_start..v_end loop v:=f1(i); end loop; print('1'); for i in v_start..v_end loop v:=f2(i); end loop; print('2'); for i in v_start..v_end loop v:=f3(i); end loop; print('3'); for i in v_start..v_end loop v:=f4(i); end loop; print('4'); end;В результате мы получим такое соотношение:
Вариант | Время(c) |
---|---|
1 вариант(c exception) | 3.03 |
2 вариант(c циклом) | 3.62 |
3 вариант(c min) | 3.34 |
4 вариант(скалярный подзапрос) | 3.10 |
declare v integer; v_start integer:=-50000; v_end integer:= 50000; l_timer integer := dbms_utility.get_time; procedure print(msg varchar2) is begin dbms_output.put_line(to_char((dbms_utility.get_time-l_timer)/100,'9990.00')||' '||msg); l_timer:=dbms_utility.get_time; end; begin print('start'); for i in v_start..v_end loop v:=f1(i); end loop; print('1'); for i in v_start..v_end loop v:=f2(i); end loop; print('2'); for i in v_start..v_end loop v:=f3(i); end loop; print('3'); for i in v_start..v_end loop v:=f4(i); end loop; print('4'); end;
Вариант | 0% | ~5% | ~10% | ~50% | ~90% |
---|---|---|---|---|---|
1 вариант(c exception) | 3.04 | 3.12 | 3.16 | 3.82 | 4.51 |
2 вариант(c циклом) | 3.18 | 3.21 | 3.20 | 3.51 | 3.85 |
3 вариант(c min) | 3.37 | 3.34 | 3.29 | 3.25 | 3.18 |
4 вариант(скалярный подзапрос) | 3.12 | 3.06 | 3.03 | 2.98 | 2.94 |
- Как видите, для данной таблички 5% исключений - это своего рода переломная точка, когда стандартный вариант с exception начинает проигрывать варианту с подзапросом(если чуть точнее, то это было на ~4.5%), и ~10% другим двум вариантам
- Варианты с min и циклом в целом хуже варианта с подзапросом.
- Варианты с подзапросом и min ускоряются с увеличением количества "пустых" запросов.
Comments
Отправить комментарий