Mat*_*sen 2 oracle plsql oracle10g
我的最后一个问题让我思考.
1)
SELECT COUNT(*) INTO count FROM foo WHERE bar = 123;
IF count > 0 THEN
SELECT a INTO var FROM foo WHERE bar = 123;
-- do stuff
ELSE
-- do other stuff
END IF;
Run Code Online (Sandbox Code Playgroud)
2)
BEGIN
SELECT a INTO var FROM foo where bar = 123;
-- do stuff
EXCEPTION
WHEN no_data_found THEN
--do other stuff
END ;
Run Code Online (Sandbox Code Playgroud)
我假设2号更快,因为它需要少一次数据库.
是否存在1个优越的情况,我不在考虑?
编辑:我会让这个问题再挂几天,在答案之前收集一些更多的选票.
如果您使用问题中的确切查询,那么第一个变体当然会变慢,因为它必须计算满足条件的表中的所有记录.
它必须作为
SELECT COUNT(*) INTO row_count FROM foo WHERE bar = 123 and rownum = 1;
Run Code Online (Sandbox Code Playgroud)
要么
select 1 into row_count from dual where exists (select 1 from foo where bar = 123);
Run Code Online (Sandbox Code Playgroud)
因为检查记录存在就足够了.
当然,这两种变体都不能保证其他人不会foo在两个语句之间改变某些内容,但如果这种检查是更复杂场景的一部分,那么这不是问题.只要考虑某人在执行某些涉及所选值的操作foo.a后选择其值后更改值的情况.因此,在复杂的场景中,可以更好地处理应用程序逻辑级别的并发问题.
执行原子操作最好使用单个SQL语句.varvar
上述任何变体都需要在SQL和PL/SQL之间进行2次上下文切换以及2次查询,因此在表中找到行的情况下执行速度比下面描述的任何变量要慢.
还有另一种变体来检查行的存在而无例外:
select max(a), count(1) into var, row_count
from foo
where bar = 123 and rownum < 3;
Run Code Online (Sandbox Code Playgroud)
如果row_count = 1,则只有一行满足条件.
有时它只能检查是否存在,因为它的唯一约束foo保证了没有重复的bar值foo.例如bar是主键.
在这种情况下,可以简化查询:
select max(a) into var from foo where bar = 123;
if(var is not null) then
...
end if;
Run Code Online (Sandbox Code Playgroud)
或使用游标处理值:
for cValueA in (
select a from foo where bar = 123
) loop
...
end loop;
Run Code Online (Sandbox Code Playgroud)
下一个变体来自链接,由@ user272735在他的回答中提供:
select
(select a from foo where bar = 123)
into var
from dual;
Run Code Online (Sandbox Code Playgroud)
根据我的经验,在大多数情况下,没有异常块的任何变量比具有异常的变量更快,但如果此类块的执行次数较低,则最好使用异常块处理no_data_found和too_many_rows异常来提高代码可读性.
选择使用异常或不使用异常的正确方法是问一个问题"这种情况对于申请是否正常?".如果找不到行并且它是可以处理的预期情况(例如,添加新行或从另一个地方获取数据等)最好避免异常.如果它是意外的并且无法修复某种情况,那么捕获异常以自定义错误消息,将其写入事件日志并重新抛出,或者根本不捕获它.
为了比较性能,只需在你的系统上做一个简单的测试用例,这两个变量都被多次调用并进行比较.
更重要的是,在90%的应用中,这个问题更具理论性而非实际性,因为还有许多其他性能问题来源必须首先考虑.
更新
我在SQLFiddle网站上通过这个页面重现了一些示例,并进行了一些修改(链接).
结果证明,选择的变体dual表现最佳:大多数查询成功时开销较小,丢失行数增加时性能最低.
如果所有查询都失败,那么使用count()和两个查询的惊人变体显示最佳结果.
| FNAME | LOOP_COUNT | ALL_FAILED | ALL_SUCCEED | variant name |
----------------------------------------------------------------
| f1 | 2000 | 2.09 | 0.28 | exception |
| f2 | 2000 | 0.31 | 0.38 | cursor |
| f3 | 2000 | 0.26 | 0.27 | max() |
| f4 | 2000 | 0.23 | 0.28 | dual |
| f5 | 2000 | 0.22 | 0.58 | count() |
-- FNAME - tested function name
-- LOOP_COUNT - number of loops in one test run
-- ALL_FAILED - time in seconds if all tested rows missed from table
-- ALL_SUCCEED - time in seconds if all tested rows found in table
-- variant name - short name of tested variant
Run Code Online (Sandbox Code Playgroud)
下面是测试环境和测试脚本的设置代码.
create table t_test(a, b)
as
select level,level from dual connect by level<=1e5
/
insert into t_test(a, b) select null, level from dual connect by level < 100
/
create unique index x_text on t_test(a)
/
create table timings(
fname varchar2(10),
loop_count number,
exec_time number
)
/
create table params(pstart number, pend number)
/
-- loop bounds
insert into params(pstart, pend) values(1, 2000)
/
Run Code Online (Sandbox Code Playgroud)
- f1 - 异常处理
create or replace function f1(p in number) return number
as
res number;
begin
select b into res
from t_test t
where t.a=p and rownum = 1;
return res;
exception when no_data_found then
return null;
end;
/
Run Code Online (Sandbox Code Playgroud)
- f2 - 游标循环
create or replace function f2(p in number) return number
as
res number;
begin
for rec in (select b from t_test t where t.a=p and rownum = 1) loop
res:=rec.b;
end loop;
return res;
end;
/
Run Code Online (Sandbox Code Playgroud)
- f3 - max()
create or replace function f3(p in number) return number
as
res number;
begin
select max(b) into res
from t_test t
where t.a=p and rownum = 1;
return res;
end;
/
Run Code Online (Sandbox Code Playgroud)
- f4 - 从双选择中选择字段
create or replace function f4(p in number) return number
as
res number;
begin
select
(select b from t_test t where t.a=p and rownum = 1)
into res
from dual;
return res;
end;
/
Run Code Online (Sandbox Code Playgroud)
- f5 - 检查count()然后得到值
create or replace function f5(p in number) return number
as
res number;
cnt number;
begin
select count(*) into cnt
from t_test t where t.a=p and rownum = 1;
if(cnt = 1) then
select b into res from t_test t where t.a=p;
end if;
return res;
end;
/
Run Code Online (Sandbox Code Playgroud)
测试脚本:
declare
v integer;
v_start integer;
v_end integer;
vStartTime number;
begin
select pstart, pend into v_start, v_end from params;
vStartTime := dbms_utility.get_cpu_time;
for i in v_start .. v_end loop
v:=f1(i);
end loop;
insert into timings(fname, loop_count, exec_time)
values ('f1', v_end-v_start+1, (dbms_utility.get_cpu_time - vStartTime)/100) ;
end;
/
declare
v integer;
v_start integer;
v_end integer;
vStartTime number;
begin
select pstart, pend into v_start, v_end from params;
vStartTime := dbms_utility.get_cpu_time;
for i in v_start .. v_end loop
v:=f2(i);
end loop;
insert into timings(fname, loop_count, exec_time)
values ('f2', v_end-v_start+1, (dbms_utility.get_cpu_time - vStartTime)/100) ;
end;
/
declare
v integer;
v_start integer;
v_end integer;
vStartTime number;
begin
select pstart, pend into v_start, v_end from params;
vStartTime := dbms_utility.get_cpu_time;
for i in v_start .. v_end loop
v:=f3(i);
end loop;
insert into timings(fname, loop_count, exec_time)
values ('f3', v_end-v_start+1, (dbms_utility.get_cpu_time - vStartTime)/100) ;
end;
/
declare
v integer;
v_start integer;
v_end integer;
vStartTime number;
begin
select pstart, pend into v_start, v_end from params;
vStartTime := dbms_utility.get_cpu_time;
for i in v_start .. v_end loop
v:=f4(i);
end loop;
insert into timings(fname, loop_count, exec_time)
values ('f4', v_end-v_start+1, (dbms_utility.get_cpu_time - vStartTime)/100) ;
end;
/
declare
v integer;
v_start integer;
v_end integer;
vStartTime number;
begin
select pstart, pend into v_start, v_end from params;
--v_end := v_start + trunc((v_end-v_start)*2/3);
vStartTime := dbms_utility.get_cpu_time;
for i in v_start .. v_end loop
v:=f5(i);
end loop;
insert into timings(fname, loop_count, exec_time)
values ('f5', v_end-v_start+1, (dbms_utility.get_cpu_time - vStartTime)/100) ;
end;
/
select * from timings order by fname
/
Run Code Online (Sandbox Code Playgroud)