原文:http://www.runoob.com/w3cnote/mysql-stored-procedure.html
MySQL 5.0 版本開(kāi)始支持存儲(chǔ)過(guò)程镶骗。
存儲(chǔ)過(guò)程(Stored Procedure)是一種在數(shù)據(jù)庫(kù)中存儲(chǔ)復(fù)雜程序哀澈,以便外部程序調(diào)用的一種數(shù)據(jù)庫(kù)對(duì)象。
存儲(chǔ)過(guò)程是為了完成特定功能的SQL語(yǔ)句集碍庵,經(jīng)編譯創(chuàng)建并保存在數(shù)據(jù)庫(kù)中,用戶可通過(guò)指定存儲(chǔ)過(guò)程的名字并給定參數(shù)(需要時(shí))來(lái)調(diào)用執(zhí)行悟狱。
存儲(chǔ)過(guò)程思想上很簡(jiǎn)單静浴,就是數(shù)據(jù)庫(kù) SQL 語(yǔ)言層面的代碼封裝與重用。
優(yōu)點(diǎn)
存儲(chǔ)過(guò)程可封裝挤渐,并隱藏復(fù)雜的商業(yè)邏輯苹享。
存儲(chǔ)過(guò)程可以回傳值,并可以接受參數(shù)浴麻。
存儲(chǔ)過(guò)程無(wú)法使用 SELECT 指令來(lái)運(yùn)行得问,因?yàn)樗亲映绦颍c查看表软免,數(shù)據(jù)表或用戶定義函數(shù)不同宫纬。
存儲(chǔ)過(guò)程可以用在數(shù)據(jù)檢驗(yàn),強(qiáng)制實(shí)行商業(yè)邏輯等膏萧。
缺點(diǎn)
存儲(chǔ)過(guò)程漓骚,往往定制化于特定的數(shù)據(jù)庫(kù)上蝌衔,因?yàn)橹С值木幊陶Z(yǔ)言不同。當(dāng)切換到其他廠商的數(shù)據(jù)庫(kù)系統(tǒng)時(shí)蝌蹂,需要重寫(xiě)原有的存儲(chǔ)過(guò)程噩斟。
存儲(chǔ)過(guò)程的性能調(diào)校與撰寫(xiě),受限于各種數(shù)據(jù)庫(kù)系統(tǒng)孤个。
一剃允、存儲(chǔ)過(guò)程的創(chuàng)建和調(diào)用
存儲(chǔ)過(guò)程就是具有名字的一段代碼,用來(lái)完成一個(gè)特定的功能齐鲤。
創(chuàng)建的存儲(chǔ)過(guò)程保存在數(shù)據(jù)庫(kù)的數(shù)據(jù)字典中斥废。
創(chuàng)建存儲(chǔ)過(guò)程
···
CREATE? ? [DEFINER = { user | CURRENT_USER }]
PROCEDURE sp_name ([proc_parameter[,...]])? ? [characteristic ...] routine_body proc_parameter:
? ? [ IN | OUT | INOUT ] param_name type characteristic:
? ? COMMENT 'string'? | LANGUAGE SQL? | [NOT] DETERMINISTIC? | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
? | SQL SECURITY { DEFINER | INVOKER }
routine_body:
Valid SQL routine statement
[begin_label:] BEGIN [statement_list]
……END [end_label]
···
MYSQL 存儲(chǔ)過(guò)程中的關(guān)鍵語(yǔ)法
聲明語(yǔ)句結(jié)束符,可以自定義:
DELIMITER $$或DELIMITER //
聲明存儲(chǔ)過(guò)程:
CREATE PROCEDURE demo_in_parameter(IN p_in int)? ? ?
存儲(chǔ)過(guò)程開(kāi)始和結(jié)束符號(hào):
BEGIN .... END? ?
變量賦值:
SET @p_in=1?
變量定義:
DECLARE l_int int unsigned default 4000000;
創(chuàng)建mysql存儲(chǔ)過(guò)程佳遂、存儲(chǔ)函數(shù):
create procedure 存儲(chǔ)過(guò)程名(參數(shù))
存儲(chǔ)過(guò)程體:
create function 存儲(chǔ)函數(shù)名(參數(shù))
實(shí)例
創(chuàng)建數(shù)據(jù)庫(kù)营袜,備份數(shù)據(jù)表用于示例操作:
mysql> create database db1;mysql> use db1;? ? mysql> create table PLAYERS as select * from TENNIS.PLAYERS;mysql> create table MATCHES? as select * from TENNIS.MATCHES;
下面是存儲(chǔ)過(guò)程的例子,刪除給定球員參加的所有比賽:
mysql> delimiter $$ #將語(yǔ)句的結(jié)束符號(hào)從分號(hào);臨時(shí)改為兩個(gè)$$(可以是自定義)mysql> CREATE PROCEDURE delete_matches(IN p_playerno INTEGER)? ? -> BEGIN? ? -> DELETE FROM MATCHES? ? ->? ? WHERE playerno = p_playerno;
? ? -> END$$Query OK, 0 rows affected (0.01 sec) mysql> delimiter; #將語(yǔ)句的結(jié)束符號(hào)恢復(fù)為分號(hào)
解析:默認(rèn)情況下丑罪,存儲(chǔ)過(guò)程和默認(rèn)數(shù)據(jù)庫(kù)相關(guān)聯(lián)荚板,如果想指定存儲(chǔ)過(guò)程創(chuàng)建在某個(gè)特定的數(shù)據(jù)庫(kù)下,那么在過(guò)程名前面加數(shù)據(jù)庫(kù)名做前綴吩屹。 在定義過(guò)程時(shí)跪另,使用?DELIMITER $$?命令將語(yǔ)句的結(jié)束符號(hào)從分號(hào)?;?臨時(shí)改為兩個(gè)?$$,使得過(guò)程體中使用的分號(hào)被直接傳遞到服務(wù)器煤搜,而不會(huì)被客戶端(如mysql)解釋免绿。
調(diào)用存儲(chǔ)過(guò)程:
call sp_name[(傳參)];
mysql> select * from MATCHES;
+---------+--------+----------+-----+------+
| MATCHNO | TEAMNO | PLAYERNO | WON | LOST |
+---------+--------+----------+-----+------+
|? ? ? 1 |? ? ? 1 |? ? ? ? 6 |? 3 |? ? 1 |
|? ? ? 7 |? ? ? 1 |? ? ? 57 |? 3 |? ? 0 |
|? ? ? 8 |? ? ? 1 |? ? ? ? 8 |? 0 |? ? 3 |
|? ? ? 9 |? ? ? 2 |? ? ? 27 |? 3 |? ? 2 |
|? ? ? 11 |? ? ? 2 |? ? ? 112 |? 2 |? ? 3 |
+---------+--------+----------+-----+------+5 rows in set (0.00 sec) mysql> call delete_matches(57);Query OK, 1 row affected (0.03 sec) mysql> select * from MATCHES;
+---------+--------+----------+-----+------+
| MATCHNO | TEAMNO | PLAYERNO | WON | LOST |
+---------+--------+----------+-----+------+
|? ? ? 1 |? ? ? 1 |? ? ? ? 6 |? 3 |? ? 1 |
|? ? ? 8 |? ? ? 1 |? ? ? ? 8 |? 0 |? ? 3 |
|? ? ? 9 |? ? ? 2 |? ? ? 27 |? 3 |? ? 2 |
|? ? ? 11 |? ? ? 2 |? ? ? 112 |? 2 |? ? 3 |
+---------+--------+----------+-----+------+4 rows in set (0.00 sec)
解析:在存儲(chǔ)過(guò)程中設(shè)置了需要傳參的變量p_playerno,調(diào)用存儲(chǔ)過(guò)程的時(shí)候擦盾,通過(guò)傳參將57賦值給p_playerno嘲驾,然后進(jìn)行存儲(chǔ)過(guò)程里的SQL操作。
存儲(chǔ)過(guò)程體
存儲(chǔ)過(guò)程體包含了在過(guò)程調(diào)用時(shí)必須執(zhí)行的語(yǔ)句迹卢,例如:dml辽故、ddl語(yǔ)句,if-then-else和while-do語(yǔ)句腐碱、聲明變量的declare語(yǔ)句等
過(guò)程體格式:以begin開(kāi)始誊垢,以end結(jié)束(可嵌套)
BEGIN BEGIN BEGIN statements;
END ENDEND
注意:每個(gè)嵌套塊及其中的每條語(yǔ)句,必須以分號(hào)結(jié)束症见,表示過(guò)程體結(jié)束的begin-end塊(又叫做復(fù)合語(yǔ)句compound statement)喂走,則不需要分號(hào)。
為語(yǔ)句塊貼標(biāo)簽:
[begin_label:] BEGIN [statement_list]END [end_label]
例如:
label1: BEGIN label2: BEGIN label3: BEGIN statements;
END label3 ;
END label2;END label1
標(biāo)簽有兩個(gè)作用:
1谋作、增強(qiáng)代碼的可讀性
2芋肠、在某些語(yǔ)句(例如:leave和iterate語(yǔ)句),需要用到標(biāo)簽
二遵蚜、存儲(chǔ)過(guò)程的參數(shù)
MySQL存儲(chǔ)過(guò)程的參數(shù)用在存儲(chǔ)過(guò)程的定義业栅,共有三種參數(shù)類型,IN,OUT,INOUT,形式如:
CREATEPROCEDURE 存儲(chǔ)過(guò)程名([[IN |OUT |INOUT ] 參數(shù)名 數(shù)據(jù)類形...])
IN 輸入?yún)?shù):表示調(diào)用者向過(guò)程傳入值(傳入值可以是字面量或變量)
OUT 輸出參數(shù):表示過(guò)程向調(diào)用者傳出值(可以返回多個(gè)值)(傳出值只能是變量)
INOUT 輸入輸出參數(shù):既表示調(diào)用者向過(guò)程傳入值秒咐,又表示過(guò)程向調(diào)用者傳出值(值只能是變量)
1、in 輸入?yún)?shù)
mysql> delimiter $$mysql> create procedure in_param(in p_in int)? ? -> begin? ? -> select p_in;
? ? -> set p_in=2;
? ? ->? ? select P_in;
? ? -> end$$mysql> delimiter ;
mysql> set @p_in=1;
mysql> call in_param(@p_in);
+------+
| p_in |
+------+
|? ? 1 |
+------+
+------+
| P_in |
+------+
|? ? 2 |
+------+
mysql> select @p_in;
+-------+
| @p_in |
+-------+
|? ? 1 |
+-------+
以上可以看出碘裕,p_in 在存儲(chǔ)過(guò)程中被修改携取,但并不影響 @p_id 的值,因?yàn)榍罢邽榫植孔兞堪锟住⒑笳邽槿肿兞俊?/p>
2雷滋、out輸出參數(shù)
mysql> delimiter //mysql> create procedure out_param(out p_out int)? ? ->? begin? ? ->? ? select p_out;
? ? ->? ? set p_out=2;
? ? ->? ? select p_out;
? ? ->? end? ? -> //mysql> delimiter ;
mysql> set @p_out=1;
mysql> call out_param(@p_out);
+-------+
| p_out |
+-------+
|? NULL |
+-------+
#因?yàn)閛ut是向調(diào)用者輸出參數(shù),不接收輸入的參數(shù)文兢,所以存儲(chǔ)過(guò)程里的p_out為null+-------+
| p_out |
+-------+
|? ? 2 |
+-------+
mysql> select @p_out;
+--------+
| @p_out |
+--------+
|? ? ? 2 |
+--------+
#調(diào)用了out_param存儲(chǔ)過(guò)程晤斩,輸出參數(shù),改變了p_out變量的值
3姆坚、inout輸入?yún)?shù)
mysql> delimiter $$mysql> create procedure inout_param(inout p_inout int)? ? ->? begin? ? ->? ? select p_inout;
? ? ->? ? set p_inout=2;
? ? ->? ? select p_inout;
? ? ->? end? ? -> $$mysql> delimiter ;
mysql> set @p_inout=1;
mysql> call inout_param(@p_inout);
+---------+
| p_inout |
+---------+
|? ? ? 1 |
+---------+
+---------+
| p_inout |
+---------+
|? ? ? 2 |
+---------+
mysql> select @p_inout;
+----------+
| @p_inout |
+----------+
|? ? ? ? 2 |
+----------+#調(diào)用了inout_param存儲(chǔ)過(guò)程澳泵,接受了輸入的參數(shù),也輸出參數(shù)兼呵,改變了變量
注意:
1兔辅、如果過(guò)程沒(méi)有參數(shù),也必須在過(guò)程名后面寫(xiě)上小括號(hào)例:
CREATE PROCEDURE sp_name ([proc_parameter[,...]]) ……
2击喂、確保參數(shù)的名字不等于列的名字维苔,否則在過(guò)程體中,參數(shù)名被當(dāng)做列名來(lái)處理
建議:
輸入值使用in參數(shù)懂昂。
返回值使用out參數(shù)介时。
inout參數(shù)就盡量的少用。
三凌彬、變量
1. 變量定義
局部變量聲明一定要放在存儲(chǔ)過(guò)程體的開(kāi)始:
DECLAREvariable_name [,variable_name...] datatype [DEFAULT value];
其中沸柔,datatype 為 MySQL 的數(shù)據(jù)類型,如: int, float, date,varchar(length)
例如:
DECLARE l_int int unsigned default 4000000;? DECLARE l_numeric number(8,2) DEFAULT 9.95;? DECLARE l_date date DEFAULT '1999-12-31';? DECLARE l_datetime datetime DEFAULT '1999-12-31 23:59:59';? DECLARE l_varchar varchar(255) DEFAULT 'This will not be padded';
2. 變量賦值
SET 變量名 = 表達(dá)式值 [,variable_name = expression ...]
3. 用戶變量
在MySQL客戶端使用用戶變量:
mysql > SELECT 'Hello World' into @x;? mysql > SELECT @x;?
+-------------+?
|? @x? ? ? ? |?
+-------------+?
| Hello World |?
+-------------+? mysql > SET @y='Goodbye Cruel World';? mysql > SELECT @y;?
+---------------------+?
|? ? @y? ? ? ? ? ? ? |?
+---------------------+?
| Goodbye Cruel World |?
+---------------------+?
mysql > SET @z=1+2+3;? mysql > SELECT @z;?
+------+?
| @z? |?
+------+?
|? 6? |?
+------+
在存儲(chǔ)過(guò)程中使用用戶變量
mysql > CREATE PROCEDURE GreetWorld( ) SELECT CONCAT(@greeting,' World');? mysql > SET @greeting='Hello';? mysql > CALL GreetWorld( );?
+----------------------------+?
| CONCAT(@greeting,' World') |?
+----------------------------+?
|? Hello World? ? ? ? ? ? ? |?
+----------------------------+
在存儲(chǔ)過(guò)程間傳遞全局范圍的用戶變量
mysql> CREATE PROCEDURE p1()? SET @last_procedure='p1';? mysql> CREATE PROCEDURE p2() SELECT CONCAT('Last procedure was ',@last_procedure);? mysql> CALL p1( );? mysql> CALL p2( );?
+-----------------------------------------------+?
| CONCAT('Last procedure was ',@last_proc? ? ? |?
+-----------------------------------------------+?
| Last procedure was p1? ? ? ? ? ? ? ? ? ? ? ? |?
+-----------------------------------------------+
注意:
1铲敛、用戶變量名一般以@開(kāi)頭
2褐澎、濫用用戶變量會(huì)導(dǎo)致程序難以理解及管理
四、注釋
MySQL 存儲(chǔ)過(guò)程可使用兩種風(fēng)格的注釋
兩個(gè)橫桿--:該風(fēng)格一般用于單行注釋原探。
c 風(fēng)格: 一般用于多行注釋乱凿。
例如:
mysql > DELIMITER //? mysql > CREATE PROCEDURE proc1 --name存儲(chǔ)過(guò)程名?
? ? -> (IN parameter1 INTEGER)?
? ? -> BEGIN?
? ? -> DECLARE variable1 CHAR(10);?
? ? -> IF parameter1 = 17 THEN?
? ? -> SET variable1 = 'birds';?
? ? -> ELSE
? ? -> SET variable1 = 'beasts';?
? ? -> END IF;?
? ? -> INSERT INTO table1 VALUES (variable1);?
? ? -> END?
? ? -> //? mysql > DELIMITER ;
MySQL存儲(chǔ)過(guò)程的調(diào)用
用call和你過(guò)程名以及一個(gè)括號(hào)顽素,括號(hào)里面根據(jù)需要咽弦,加入?yún)?shù),參數(shù)包括輸入?yún)?shù)胁出、輸出參數(shù)型型、輸入輸出參數(shù)。具體的調(diào)用方法可以參看上面的例子全蝶。
MySQL存儲(chǔ)過(guò)程的查詢
我們像知道一個(gè)數(shù)據(jù)庫(kù)下面有那些表闹蒜,我們一般采用?showtables;?進(jìn)行查看寺枉。那么我們要查看某個(gè)數(shù)據(jù)庫(kù)下面的存儲(chǔ)過(guò)程,是否也可以采用呢绷落?答案是姥闪,我們可以查看某個(gè)數(shù)據(jù)庫(kù)下面的存儲(chǔ)過(guò)程,但是是另一鐘方式砌烁。
我們可以用以下語(yǔ)句進(jìn)行查詢:
selectname from mysql.proc where db='數(shù)據(jù)庫(kù)名';或者selectroutine_name from information_schema.routines where routine_schema='數(shù)據(jù)庫(kù)名';或者showprocedure status where db='數(shù)據(jù)庫(kù)名';
如果我們想知道筐喳,某個(gè)存儲(chǔ)過(guò)程的詳細(xì),那我們又該怎么做呢函喉?是不是也可以像操作表一樣用describe 表名進(jìn)行查看呢避归?
答案是:我們可以查看存儲(chǔ)過(guò)程的詳細(xì),但是需要用另一種方法:
SHOWCREATE PROCEDURE 數(shù)據(jù)庫(kù).存儲(chǔ)過(guò)程名;
就可以查看當(dāng)前存儲(chǔ)過(guò)程的詳細(xì)管呵。
MySQL存儲(chǔ)過(guò)程的修改
ALTER PROCEDURE
更改用 CREATE PROCEDURE 建立的預(yù)先指定的存儲(chǔ)過(guò)程梳毙,其不會(huì)影響相關(guān)存儲(chǔ)過(guò)程或存儲(chǔ)功能。
MySQL存儲(chǔ)過(guò)程的刪除
刪除一個(gè)存儲(chǔ)過(guò)程比較簡(jiǎn)單捐下,和刪除表一樣:
DROPPROCEDURE
從 MySQL 的表格中刪除一個(gè)或多個(gè)存儲(chǔ)過(guò)程账锹。
MySQL存儲(chǔ)過(guò)程的控制語(yǔ)句
(1). 變量作用域
內(nèi)部的變量在其作用域范圍內(nèi)享有更高的優(yōu)先權(quán)冠跷,當(dāng)執(zhí)行到 end噪窘。變量時(shí)斜筐,內(nèi)部變量消失请毛,此時(shí)已經(jīng)在其作用域外榨乎,變量不再可見(jiàn)了节槐,應(yīng)為在存儲(chǔ)過(guò)程外再也不能找到這個(gè)申明的變量鸭限,但是你可以通過(guò) out 參數(shù)或者將其值指派給會(huì)話變量來(lái)保存其值闰蚕。
mysql > DELIMITER //? mysql > CREATE PROCEDURE proc3()?
? ? -> begin
? ? -> declare x1 varchar(5) default 'outer';?
? ? -> begin
? ? -> declare x1 varchar(5) default 'inner';?
? ? ? -> select x1;?
? ? ? -> end;?
? ? ? -> select x1;?
? ? -> end;?
? ? -> //? mysql > DELIMITER ;
(2). 條件語(yǔ)句
1. if-then-else 語(yǔ)句
mysql > DELIMITER //? mysql > CREATE PROCEDURE proc2(IN parameter int)?
? ? -> begin
? ? -> declare var int;?
? ? -> set var=parameter+1;?
? ? -> if var=0 then
? ? -> insert into t values(17);?
? ? -> end if;?
? ? -> if parameter=0 then
? ? -> update t set s1=s1+1;?
? ? -> else
? ? -> update t set s1=s1+2;?
? ? -> end if;?
? ? -> end;?
? ? -> //? mysql > DELIMITER ;
2. case語(yǔ)句:
mysql > DELIMITER //? mysql > CREATE PROCEDURE proc3 (in parameter int)?
? ? -> begin
? ? -> declare var int;?
? ? -> set var=parameter+1;?
? ? -> case var?
? ? -> when 0 then?
? ? -> insert into t values(17);?
? ? -> when 1 then?
? ? -> insert into t values(18);?
? ? -> else?
? ? -> insert into t values(19);?
? ? -> end case;?
? ? -> end;?
? ? -> //? mysql > DELIMITER ; case? ? when var=0 then? ? ? ? insert into t values(30);
? ? when var>0 then? ? when var<0 then? ? elseend case
(3). 循環(huán)語(yǔ)句
1. while ···· end while
mysql > DELIMITER //? mysql > CREATE PROCEDURE proc4()?
? ? -> begin
? ? -> declare var int;?
? ? -> set var=0;?
? ? -> while var<6 do?
? ? -> insert into t values(var);?
? ? -> set var=var+1;?
? ? -> end while;?
? ? -> end;?
? ? -> //? mysql > DELIMITER ;
while 條件 do? ? --循環(huán)體endwhile
2. repeat···· end repea
它在執(zhí)行操作后檢查結(jié)果排抬,而 while 則是執(zhí)行前進(jìn)行檢查懂从。
mysql > DELIMITER //? mysql > CREATE PROCEDURE proc5 ()?
? ? -> begin?
? ? -> declare v int;?
? ? -> set v=0;?
? ? -> repeat?
? ? -> insert into t values(v);?
? ? -> set v=v+1;?
? ? -> until v>=5?
? ? -> end repeat;?
? ? -> end;?
? ? -> //? mysql > DELIMITER ;
repeat
? ? --循環(huán)體until 循環(huán)條件? end repeat;
3. loop ·····endloop
loop 循環(huán)不需要初始條件,這點(diǎn)和 while 循環(huán)相似蹲蒲,同時(shí)和 repeat 循環(huán)一樣不需要結(jié)束條件, leave 語(yǔ)句的意義是離開(kāi)循環(huán)番甩。
mysql > DELIMITER //? mysql > CREATE PROCEDURE proc6 ()?
? ? -> begin
? ? -> declare v int;?
? ? -> set v=0;?
? ? -> LOOP_LABLE:loop?
? ? -> insert into t values(v);?
? ? -> set v=v+1;?
? ? -> if v >=5 then
? ? -> leave LOOP_LABLE;?
? ? -> end if;?
? ? -> end loop;?
? ? -> end;?
? ? -> //? mysql > DELIMITER ;
4. LABLES 標(biāo)號(hào):
標(biāo)號(hào)可以用在 begin repeat while 或者 loop 語(yǔ)句前,語(yǔ)句標(biāo)號(hào)只能在合法的語(yǔ)句前面使用届搁≡笛Γ可以跳出循環(huán),使運(yùn)行指令達(dá)到復(fù)合語(yǔ)句的最后一步卡睦。
(4). ITERATE迭代
ITERATE 通過(guò)引用復(fù)合語(yǔ)句的標(biāo)號(hào),來(lái)從新開(kāi)始復(fù)合語(yǔ)句:
mysql > DELIMITER //? mysql > CREATE PROCEDURE proc10 ()?
? ? -> begin
? ? -> declare v int;?
? ? -> set v=0;?
? ? -> LOOP_LABLE:loop?
? ? -> if v=3 then?
? ? -> set v=v+1;?
? ? -> ITERATE LOOP_LABLE;?
? ? -> end if;?
? ? -> insert into t values(v);?
? ? -> set v=v+1;?
? ? -> if v>=5 then
? ? -> leave LOOP_LABLE;?
? ? -> end if;?
? ? -> end loop;?
? ? -> end;?
? ? -> //? mysql > DELIMITER ;