分享
 
 
 

oracle自定义函数的执行时间

王朝oracle·作者佚名  2008-05-31
窄屏简体版  字體: |||超大  

Oracle中的pl/sql代码会涉及到两个执行引擎,一个专门处理标准的SQL语句,另外一个处理pl/sql的过程代码,一般在引擎切换上会带来cpu额外的开销。

比如foreach循环语句和普通for循环的区别,就是foreach消除了引擎切换,一直驻留在执行标准SQL的引擎中,从而缩短了执行时间。

现在有一个客户信息表tacustomer, 包含了birthday, certificatetype, certificateno等字段,现在想要获知客户的生日信息。由于客户注册时生日等字段是可选择填写项,故大多数为空,所以要从证件号码certificateno中提取,certificatetype为证件类型,除身份证之外还有军官证、士兵证、户口本等枚举值,输入的值也不可靠,本来'0'代表身份证,但由于录入错误,使得非'0'值可能也指身份证,而'0'也可能输入的不是身份证。结合这些考虑因素,写了一个函数,输入参数为上述三个字段,输出为生日的年份。写了两个版本,在笔者的windows2003数据库服务器上(Intel(R) Xeon(R) CPU 5140 @ 2.33GHZ,4cpu,4.00GB内存),执行语句如下(tacustomer行数为200百万左右):

-- 测试简单字符串连接的执行时间

SELECT COUNT(t.certificatetype t.certificateno t.birthday)

FROM tacustomer t

WHERE 1 = 1

AND ROWNUM < 2000000

-- 测试自定义函数的执行时间

SELECT COUNT(f_extract_birthday(t.certificatetype, t.certificateno, t.birthday))

FROM tacustomer t

WHERE 1 = 1

AND ROWNUM < 2000000

执行时间如下(秒):

8.563

19.844(版本1)

57.953(版本2)

可以看出基本是3倍的关系(将上面的条件改为ROWNUM < 1000000,同样满足3倍的关系),版本1和版本2之间的区别是内部实现不同。第一代身份证15位必须全为0-9的数字,第二代18位的前17位全为数字,最后一个为数字或'X'。版本1和2对这个是否数字的校验采用了不同的方式。版本1逐个检查,版本2用cast(certno as numeric)并捕捉异常的方式,结果版本2比1快3倍。从中得出的结论,自定义函数一般性能比不上系统内建函数。

附两个版本的代码:

--版本1

create or replace FUNCTION f_extract_birthday(id in VARCHAR, birthday in VARCHAR)

RETURN VARCHAR

IS

i integer;

--id VARCHAR(18);

--birthday VARCHAR(8);

yyyy VARCHAR(4);

len SMALLINT;

trans BOOLEAN;

c SMALLINT;

ret VARCHAR(4);

val NUMERIC(18, 0);

BEGIN

i := 1;

--id := '430302810315405';

--id := '43030219810315405X';

--birthday := '19790315';

trans := FALSE;

len := length(id);

--val := CAST(id AS NUMERIC);

--dbms_output.put_line(val); if (len = 15) then

-- 检查是否全为数字,否则转换操作会抛异常,导致程序崩溃,ascii('0') = 48, ascii('9') = '57'

--for i in 1 .. 15 loop

WHILE i <= 15 LOOP

--ch := substr(id, i, 1); -- 非数字会报错

c := ascii(substr(id, i, 1));

if c < 48 or c > 57 then

exit;

end if;

i := i + 1;

end loop;

IF i = 16 THEN

trans := TRUE;

ret := '19' substr(id, 7, 2);

END IF;

end if;

if (len = 18) then

-- 2006-10-18 luocm

-- 检查是否全为数字,否则转换操作会抛异常,导致程序崩溃,ascii('0') = 48, ascii('9') = '57'

--for i in 1 .. 17 loop

WHILE i <= 17 LOOP

--ch := substr(id, i, 1); -- 非数字会报错

c := ascii(substr(id, i, 1));

if c < 48 or c > 57 then

exit;

end if;

i := i + 1;

end loop;

c := ascii(substr(id, 18, 1));

--if i <> 18 OR (c <> 88 AND c <> 120) OR c < 48 or c > 57 THEN -- 第18位为[0-9xX]

if i = 18 AND (c = 88 OR c = 120 OR c >= 48 or c <= 57) THEN -- 第18位为[0-9xX]

trans := TRUE;

ret := substr(id, 7, 4);

end if;

end if;

IF trans = FALSE THEN

i := 1;

WHILE i <= 8 LOOP

c := ascii(substr(birthday, i, 1));

if c < 48 or c > 57 then

exit;

end if;

i := i + 1;

end loop;

IF i > 8 THEN

yyyy := substr(ltrim(rtrim(birthday)), 1, 4);

IF yyyy < 1900 OR yyyy > 2000 THEN

ret := '';

ELSE

ret := yyyy;

END IF;

ELSE

ret := yyyy;

END IF;

END IF;

RETURN ret;

end f_extract_birthday;

--版本2

create or replace FUNCTION f_extract_birthday(certtype IN varchar, certno in VARCHAR, birthday in VARCHAR)

RETURN VARCHAR

IS

len SMALLINT;

c SMALLINT;

val NUMERIC(18, 0);

ret VARCHAR(4);

BEGIN

ret := '';

IF certtype = '0' THEN -- 身份证

BEGIN

len := length(certno);

if (len = 15) THEN

val := CAST(certno AS NUMERIC);

ret := '19' substr(certno, 7, 2);

ELSIF (len = 18) THEN

val := CAST(substr(certno, 1, 17) AS NUMERIC);

c := ascii(substr(certno, 18, 1));

--if i <> 18 OR (c <> 88 AND c <> 120) OR c < 48 or c > 57 THEN -- 第18位为[0-9xX]

if (c = 88 OR c = 120 OR c >= 48 or c <= 57) THEN -- 第18位为[0-9xX]

ret := substr(certno, 7, 4);

end if;

end if;

EXCEPTION

WHEN value_error THEN -- 字符串转实数错误

NULL;

END;

END IF;

BEGIN

IF ret IS NULL THEN

val := CAST(birthday AS NUMERIC);

ret := substr(birthday, 1, 4);

END IF;

EXCEPTION

WHEN value_error THEN

NULL;

END;

BEGIN

-- 判定年份是否合法,暂定[1900, 2000]区间,过几年需要修改

--IF (ret <> '') THEN

IF (ret IS NOT NULL) THEN

val := CAST(ret AS INT);

IF (val < 1900 OR val > 2000) THEN

ret := '';

END IF;

END IF;

EXCEPTION

WHEN value_error THEN -- 字符串转实数错误

ret := '';

--dbms_output.put_line(certno ',' ret);

END;

RETURN ret;

end f_extract_birthday;

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有