一、存儲過程概述
存儲過程是一個用戶定義的函數,由存儲過程語句(SPL) 和一組SQL語句組成,以可以執行代碼形式存儲在數據庫中,和表、視圖、索引等一樣,是數據庫的一種對象。
存儲過程語言SPL(Stored Procedure Language),只能用在存儲過程中,可以分成下面幾類:
1、變量定義和賦值:define,let
2、流程控制:
分支控制: if then elif else end if;
循環控制: FOR,FOREACH,WHILE,EXIT, CONTINUE
3、函數調用與返回
CALL, SYSTEM ,RETURN
4、錯誤處理和調試
TRACE, ON EXCEPTION, RAISE EXCEPTION
例子:
drop procedure count_add;--刪除存儲過程 create procedure count_add(user_name_var varchar(50) default 'administrator') --user_name_var傳遞參數變量在此定義 returning varchar(50); --返回一個字符型的值 define error_count_var integer; ----定義輸入的次數變量 select error_count into error_count_var from users where user_name=user_name_var; ----error_count默認是0,從0開始記數 let error_count_var=error_count_var 1; ----輸入一次記數加1 update users set error_count= error_count_var where user_name =user_name_var return user_name_var; --返回變量值,與returning對應。 return user_name_var WITH RESUME; ----將保證存儲過程繼續執行,所有的變量均保持原有的值 end procedure document ‘this is a error count procedure’ with listing in ’/export/home/xie/errorcount.txt‘;
將該腳本保存為count_add.sql,在unix系統下,執行如下命令建立存儲過程:
$dbaccess db1 count_add.sql
存儲過程建立在數據庫db1中,執行存儲過程可以通過dbaccess工具:
$dbaccess db1
然后進入執行狀態
execute procedure count_add('administrator');
execute procedure db@servername:count_add('administrator');
與存儲過程相關系統表
SYSPROCEDURES:記錄數據庫的所有存儲過程
SYSPROCBODY:記錄存儲過程文本
SYSPROCPLAN:記錄存儲過程執行的查詢規划
SYSPROCAUTH:記錄授予存儲過程的權限
查看存儲過程代碼、導出存儲過程
$dbschema -d dbname -f procedurename -ss
例如:
xxxdb% dbschema -d xxdb -f pro1231 DBSCHEMA Schema Utility
INFORMIX-SQL Version 9.40.FC7
Copyright IBM Corporation 1996, 2004 All rights reserved Software Serial Number AAA#B000000
create procedure "xxxuser".pro1231() returning int;
return 2007;
end procedure document "this is a test" ;
$dbschema -d dbname -f procedurename proc.sql
從系統表中查看
select data from sysprocedures, sysprocbody where datakey ="T" and sysprocedures.procname = "pro1231" and sysprocedures.procid = sysprocbody.procid;
結果顯示
data create procedure pro1231() returning int;
return 2007;
end procedure document "this is a test"
二、創建存儲過程
語法:
CREATE [DBA] PROCEDURE 過程名(參數[, 參數 [ ,...]] )
RETURNING 子句 語句塊
END PROCEDURE
document 子句
WITH LISTING IN 文件名
過程名說明創建的存儲過程的名字參數說明調用該存儲過程所需的參數個數和類型一個存儲過程可以不返回任何值,或返回一個或多個值,也可返回多組值。
返回多組值的存儲過程稱之為游標式存儲過程,對該類存儲過程,相應調用函數需作一些特殊處理。
例子:假設建立一個腳本名為proc1231.sql的文件,內容如下:
create procedure proc1231() returning int; return 2007; end procedure --end procedure后面不能加分號(;),否則會報語法錯誤 document 'this is a new procedure' --這里也不能加分號(;),ducoment子句需加雙引號或單引號。 with listing in '/export/home/user/pro.log'; --最后可以加分號(;),也可以不加
存儲過程中的語句塊由SPL語句和SQL語句組成,但不包含下面的SQL語句
CREATE DATABASE DATEBASE CLOSE DATEBASE CHECK TABLE REPAIR INFO OUTPUT LOAD UNLOAD CREATE PROCEDURE CREATE PROCEDURE FROM
document子句對存儲過程做一些說明,可省略。
WITH LISTING IN 選項說明接受編譯器輸出信息的文件名,如省略, 則編譯器不產生輸出。
向存儲過程中傳遞變量
create procedure procname(var_num integer default null) delete from proctable where colno=var_num; end procedure; execute procedure procname(11);
return語句 從存儲過程中返回0個或多個值 RETURN 語句說明的返回值的個數和類型必須與創建存儲過程時說明的返回值的個數和類型一致,或者不返回任何值,在后一情形下,所有的返
回值為空值。 WITH RESUME子句將保證存儲過程繼續執行,所有的變量均保持原有的值。該子句用於返回多組值,比如循環語句中。
create procedure proc_new() returning int,int; define num1; define num2; ..... .... return num1,num2; end procedure;
從SQL中調用存儲過程
select * from users where number=proc_new(23);
call語句
從一個存儲過程中調用另一個存儲過程
兩種格式:
CALL 過程名(參數, ...)
RETURNING 變量,...;
CALL 過程名(參數名=參數, ...)
RETURNING 變量,...;
說明:參數可以是SPL表達式或是SELECT語句,只要該語句返回單值,並且具有適當的類型和長度。如果參數個數多於被調用的存儲過程的參數,則返回錯 誤。如果參數個數少於被調用的存儲過程的參數,則未說明的參數被初始化為其缺省值(該值在創建存儲過程時說明)若無缺省值, 則返回錯誤。 RETURNING 子句說明的變量用於接收被調用存儲過程的返回值,如無返回值, 則可省略。
例如: define var_no1 int;
三、存儲過程語言
變量
局部變量 僅在本存儲過程中有效的變量。 局部變量不允許有缺省值
全局變量 在同一用戶活動期間, 存儲過程中的可被同一數據庫的其它存儲過程訪問的的變量。全局變量必須在所有使用的存儲過程中定義,並且必須給
出缺省值,實際的缺省值是第一次被訪問時定義的缺省值。
變量(局部變量)的范圍 變量在語句塊內有效, 如語句塊嵌套,則同名的內層定義覆蓋其外層定義, 內層語句執行完后,外層定義重新有效。
存儲過程表達式存儲過程表達式可以是除聚集函數表達式外的任何SQL算術表達式例子: var_value1; var_value1 var_value2;
變量定義 用DEFINE 語句定義變量,其類型可以是除SERIAL數據類型外 的所有SQL 數據類型定義TEXT 或BYTE 類型變量時, 需在其變量前加關鍵字REFERENCES, 以表明該變量並不含有真正的數據, 而只是指向數據的指針。
可以使用LIKE定義與字段類型一致的數據類型傳入參數變量的定義在create procedure procname(var_value int)定義
例子:
DEFINE i,j INT; DEFINE name VARCHAR(12); DEFINE time_stamp DATETIME YEAR TO SECOND DEFAULT CURRENT YEAR TO SCECOND; DEFINE date_value DATE; DEFINE txt REFERENCES TEXT; DEFINE by REFERENCES BYTE; DEFINE p_customer like users.customer_num;
定義全局變量
define global global_var int default 0;
局部BLOB數據類型只使用描述符:
LET DEFINE CALL SELECT
存取BLOB數據:
INSERT UPDATE RETURN(到前端應用程序)
變量賦值 四種方式
1、利用LET 語句
LET i, j=1, 0; LET var_name, var_num=(SELECT username, usernum FROM users WHERE usernum=100);
2、利用SELECT 或SELECT ... INTO 語句
SELECT username INTO var_name FROM users WHERE usernum=100;
3、利用CALL 語句
call proc_new(22) returning var_no1;
4、利用EXECUTE PROCEDURE ...INTO 語句
execute procedure proc_name(username,address into p_name,p_addr);
語句塊
create procedure pro_test() returning int; begin define i int; let i=1; end --begin,end 語句塊(顯示) retrun i; --returning,return語句塊(隱含) end procedure;
foreach循環
create procedure pro_foreach() define id int; select userid into id from users where age>30; --select語句返回大於1的行,可以理解為將select到的值放在id這個列表中一個一個執行。 update others set uservalue=345 where userid=id; end foreach; end procedure;
使用update游標
條件分支 if then elif then else end if
--------------------------------------------------
drop procedure pro_dele; create procedure pro_dele()
define p_time date; begin work; foreach curl for --update游標必須命名 select time_stamp into p_time from pro_dele_tbl where num > 100 if p_time is not null then delete from pro_del_tbl where current of curl; --刪除當前記錄 end if; end foreach; commit work; --所有修改記錄的鎖被釋放。 end procedure;
--------------------------------------------------
if語句中的表達式
If exists(select username from users where usernum = 13) then … end if if var_num > all(select usernum from users where usernum = 23) then … end if if var_name matches “A*” then … end if
循環語句while
格式: WHILE 條件語句 語句塊 END WHILE
執行過程先測試條件,若為真,則執行語句塊,否則退出循環。重復上述步驟,開始下一次循環,直到條件為假而退出循環
WHERE 與FOREACH 的區別
WHILE 循環的條件是不定的,循環次數不定,因而可能出現無窮循環 FOR和FOREACH 循環的條件是確定的,循環次數也是明確的,不可能出現無窮循環
例子
create procedure prowhile()
define i int; define sum int; let i=1; let sum=0; while i<100 let sum=sum i; let i=i 1; end while; end procedure;
循環語句for
三種格式
FOR 變量 IN (expr1 to expr2 STEP expr3)
語句塊
END FOR --expr1,expr2表示范圍,expr3表示步長,默認為1
for i=1 to 10 step 2 ... end for;
FOR 變量 = (expr1 to expr2 STEP expr3)
語句塊
END FOR --expr1,expr2表示范圍,expr3表示步長,默認為1
FOR 變量 IN (expr1, expr2,...)
語句塊
END FOR
for i in (1,2,3,4,5,7,8) ... end for;
循環的轉移
for i = 1 to 10 if i = 5 then continue for; ----------------contiune將執行下一次循環 elif i = 8 then exit for; ----------------exit for將退出循環,執行for循環的下一條語句 end if; end for;
循環語句foreach
用 FOREACH 語句可以查詢或操作一組記錄 FOREACH 隱式定義並打開一個游標
三種格式
FOREACH [WITH HOLD] SELECT ... INTO 語句
語句塊
END FOREACH;-FOREACH 游標名[WITH HOLD]
FOR SELECT ... INTO 語句
語句塊
END FOREACH;
FOREACH EXECUTE PROCEDURE 存儲過程名(參數...參數) INTO 變量[, 變量[, ...]] 語句塊 END FOREACH;
FOREACH 定義的游標在以下情況下關閉:
無行返回事務提交或回滾導致非保護游標關閉循環非正常退出(使用EXIT 或 ON EXCEPTION)
操作系統命令
system "echo" "Delete Operation Completed" "|mail judy";
數據庫系統一直等到上述命令執行完畢不能使用返回值判斷執行是否成功如執行不成功,將設置適當的ISAM錯誤代碼和SQL錯誤代碼
檢查NOTFOUND條件
create procedure protest()
define num integer; returning integer; foreach select usernum into num from users where usernum=20 --select執行失敗將不執行foreach中的語句。 return num; end foreach; return; --return沒有返回值將在前端應用中引發NOTFOUND end procedure;
遞歸調用
create procedure digui(i int defualt 1) returning int; if i <6 then return 1; end if; return n * digui(n-2); --調用自己 end procedure;
獲取serial值
create procedure seri_inst() define seri int; insert into users (user_num,user_date) values (1,"2006-01-03"); let seri = dbinfo("sqlca.sqlerrd1"); --獲取上一條insert語句中的serial值 insert into other(num,name) values(seri,"new"); end procedure;
獲取處理的記錄數
create procedure num_rows() returning int; define num_rows int; delete from orders where customer_num = 104; let num_rows = dbinfo("sqlca.sqlerrd2"); --select、update或update的記錄數 return num_rows; end procedure;
跟蹤調試語句trace
存儲過程被正確創建后,說明無語法錯誤,但有可能有邏輯錯誤 TRACE語句用於調試存儲過程, 它可以跟蹤以下存儲過程實體的值:
變量(Variables)
過程參數(Procedure arguments)
返回值(Return values)
SQL 錯誤代碼(SQL error codes)
ISAM 錯誤代碼(ISAM error codes)
TRACE 語句把跟蹤結果寫到一個文件中, 該文件由SQL語句SET DEBUG FILE指定
TRACE 語句的三種形式:
TRACE ON :打開跟蹤調試, 跟蹤所有語句
TRACE OFF :關閉跟蹤調試
TRACE PROCEDURE: 對於過程調用, 不跟蹤過程體,僅跟蹤過程的輸入和返回值。
create procedure tracepro(var_user_num int) define var_user_date date; set debug file to "/export/home/user/trace"; --設置輸出文件 trace on; --跟蹤所有執行的語句 select user_date into var_user_date from users where user_num = var_user_num; if var_user_date is null then trace "user date is null"; --執行到這里輸出user date is null execute procedure other((var_user_num ); end if; trace off; --關閉跟蹤 end procedure;
四 存儲過程中的異常處理
沒有異常處理
create procedure yichang () returning int; define var_num integer; let var_num = "jack"; --------存儲過程到這將會出錯 return var_num; end procedure;
異常處理
create procedure err_deal() define sql_err int; define isam_err int; define error_info char(100); on exception set sql_err, isam_err, error_info call error_rout(sql_err,isam_err,error_info); end exception; end procedure;
異常捕獲:ON EXCEPTION
用ON EXCEPTION語句捕獲一個或一組特定的異常(即錯誤),用錯誤號標識。
ON EXCEPTION 語句與RAISE EXCEPTION 語句一起提供存儲過程語言(SPL)的錯誤 捕獲和恢復機制。
在一個語句塊內可以定義多個 ON EXCEPTION 語句。
被捕獲的異常可以是系統異常或用戶定義的異常。
一旦異常被捕獲,錯誤狀態即被清除。
ON EXCEPTION 語句的位置:
ON EXCEPTION 語句是一聲明性而非執行性語句, 故應位於任何執行語句之前, 而位於DEFINE 語句之后。
格式
ON EXCEPTION IN (錯誤號,...)
SET SQL 錯誤變量 ISAM 錯誤文本變量
語句塊
END EXCEPTION [WITH RESUME]
IN 子句說明欲捕獲的錯誤號, 缺省時捕獲所有的錯誤號。
SET 子句接收錯誤號和錯誤文本的變量,該語句可省略。
SQL 錯誤變量: 說明接收SQL 錯誤號的變量
ISAM錯誤變量: 說明接收ISAM錯誤號的變量
錯誤文本變量: 說明接收與SQL錯誤號對應的錯誤文本的變量 WITH RESUME 關鍵字用於把控制轉向到捕獲的錯誤被處理后的緊接發生異常語句后的語句,其效果相當於異常被處理后程序繼續執行下去。
WITH RESUME 可以省略。
捕捉特定的錯誤
create procedure err_deal() define sql_err int; define isam_err int; define error_info char(100); on exception set sql_err, isam_err, error_info call error_rout(sql_err,isam_err,error_info); end exception; -----其他錯誤的捕捉 on exception (-206) call new_tab(); end exception; -----表不在數據庫中的錯誤在這里捕捉 end procedure;
異常處理后繼續執行
on exception (-206) call new_tab(); end exception with resume; select new froom tab; --出錯將繼續執行 let nex=9;
WITH RESUME將繼續執行出錯行后面的語句
如果沒有WITH RESUME將繼續執行下一次循環或下一個語句塊,如果有的話。如果沒有語句塊,則過程將結束。
異常捕獲:RAISE EXCEPTION
用RAISE EXECPTION 語句模擬異常的產生,該異常可被ON EXECTPION語句捕獲。
格式: RAISE EXCEPTION SQL 錯誤號, ISAM 錯誤號, 錯誤文本變量 SQL錯誤號和ISAM錯誤號均可是SPL表達式,且其計算結果是一個常數(錯誤號)例子: RAISE EXCEPTION -99999, 0, --Broke the Rule
create procedure err_deal() define sql_err int; define isam_err int; define error_info char(100); on exception set sql_err, isam_err, error_info call error_rout(sql_err,isam_err,error_info); raise exception sql_err, isam_err, error_info; ----------------RAISE EXCEPTION引發一個人工的錯誤信息 end exception; end procedure;
存儲過程的權限
兩類存儲過程 DBA 權限的存儲過程所有者權限的存儲過程
下列用戶可以創建存儲過程 具有RESOURCE 權限的用戶可以創建存儲過程