oracle自定義函式的執行時間

2021-04-13 01:15:19 字數 4961 閱讀 7994

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

isi integer;

--id varchar(18);

--birthday varchar(8);

yyyy varchar(4);

len **allint;

trans boolean;

c **allint;

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

islen **allint;

c **allint;

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;

自定義異常 執行時期和編譯時期的區別

1.自定義異常一 繼承runtmeexcepyion,即執行期間異常 由於我們自定義的是執行期間的異常,我們丟擲異常,程式始終不作處理,程式編譯的時候不會報錯,但是執行的時候會報錯,因此會中斷程式.2.自定義異常一 繼承excepyion,即編譯期間異常 跟上面相反,丟擲異常,必須要處理,不然不會通...

Oracle自定義函式

語法如下 create or replace function function name argment type,argment type return return type 返回資料的型別 變數的申明,比如 stryuan varchar2 150 begin function body 函...

oracle 自定義函式

下面是乙個前輩寫的判斷是否是手機號的函式 create or replace function ismobile pmsg varchar2 return number isvprefix varchar2 20 vlen number begin vlen lengthb pmsg if vlen...