存儲過程和存儲函數類似於面向對象程式設計語言中的方法,可以簡化代碼,提高代碼的重用性。本文主要介紹如何創建存儲過程和存儲函數,以及存儲過程與函數的使用、修改、刪除等操作。 ...
1 引言
2 存儲過程與存儲函數
3 創建與修改存儲過程和函數
4 控制語句
5 查看存儲過程和函數
6 刪除存儲過程和函數
7 總結
1 引言
存儲過程和存儲函數類似於面向對象程式設計語言中的方法,可以簡化代碼,提高代碼的重用性。本文主要介紹如何創建存儲過程和存儲函數,以及存儲過程與函數的使用、修改、刪除等操作。
2 存儲過程與存儲函數
MySQL中提供存儲過程與存儲函數機制,我們姑且將存儲過程和存儲函數合稱為存儲程式。與一般的SQL語句需要先編譯然後立即執行不同,存儲程式是一組為了完成特定功能的SQL語句集,經編譯後存儲在資料庫中,當用戶通過指定存儲程式的名字並給定參數(如果該存儲程式帶有參數)來調用才會執行。
存儲程式就是一條或者多條SQL語句和控制語句的集合,我們可以將其看作MySQL的批處理文件,當然,其作用不僅限於批處理。當想要在不同的應用程式或平臺上執行相同的功能一段程式或者封裝特定功能時,存儲程式是非常有用的。資料庫中的存儲程式可以看做是面向對編程中面向對象方法,它允許控制數據的訪問方式。
存儲函數與存儲過程有如下區別:
(1)存儲函數的限制比較多,例如不能用臨時表,只能用表變數,而存儲過程的限制較少,存儲過程的實現功能要複雜些,而函數的實現功能針對性比較強。
(2)返回值不同。存儲函數必須有返回值,且僅返回一個結果值;存儲過程可以沒有返回值,但是能返回結果集(out,inout)。
(3)調用時的不同。存儲函數嵌入在SQL中使用,可以在select 存儲函數名(變數值);存儲過程通過call語句調用 call 存儲過程名。
(4)參數的不同。存儲函數的參數類型類似於IN參數,沒有類似於OUT和INOUT的參數。存儲過程的參數類型有三種,IN、out和INOUT:
a. in:數據只是從外部傳入內部使用(值傳遞),可以是數值也可以是變數
b. out:只允許過程內部使用(不用外部數據),給外部使用的(引用傳遞:外部的數據會被先清空才會進入到內部),只能是變數
c. inout:外部可以在內部使用,內部修改的也可以給外部使用,典型的引用 傳遞,只能傳遞變數。
3 存儲過程
3.1 創建存儲過程
創建存儲過程語法結構如下:
CREATE PROCEDURE 過程名([[IN|OUT|INOUT] 參數名 數據類型[,[IN|OUT|INOUT] 參數名 數據類型…]]) [特性 ...] BEGIN 過程體 END
CREATE PROCEDURE是用來創建存儲過程的關鍵字;[IN|OUT|INOUT]是參數的輸入輸出類型,IN表示輸入參數,OUT表示輸出參數,INOUT表示既可以輸入也可以輸出;過程體是包含若幹SQL語句或流程式控制制語句的集合,可以用BEGIN…END來包裹。
在演示如果創建存儲過程之前(emp表、dept表),先創建兩個數據表,本文所有演示操作都基於這兩個表來進行,創建表與插入數據SQL語句如下:
emp表:
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for emp -- ---------------------------- DROP TABLE IF EXISTS `emp`; CREATE TABLE `emp` ( `empno` int(4) NOT NULL, `ename` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL, `job` varchar(9) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL, `mgr` int(4) NULL DEFAULT NULL, `hiredate` date NULL DEFAULT NULL, `sal` float(7, 2) NULL DEFAULT NULL, `comm` float(7, 2) NULL DEFAULT NULL, `deptno` int(2) NULL DEFAULT NULL, PRIMARY KEY (`empno`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of emp -- ---------------------------- INSERT INTO `emp` VALUES (7369, 'SMITH', 'CLERK', 7902, '1998-12-17', 800.00, NULL, 20); INSERT INTO `emp` VALUES (7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600.00, 300.00, 30); INSERT INTO `emp` VALUES (7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250.00, 500.00, 30); INSERT INTO `emp` VALUES (7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975.00, NULL, 20); INSERT INTO `emp` VALUES (7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250.00, 1400.00, 30); INSERT INTO `emp` VALUES (7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850.00, NULL, 30); INSERT INTO `emp` VALUES (7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450.00, NULL, 10); INSERT INTO `emp` VALUES (7788, 'SCOTT', 'ANALYST', 7566, '1981-11-17', 3000.00, NULL, 20); INSERT INTO `emp` VALUES (7839, 'KING', 'PRESIDENT', NULL, '1981-11-17', 5000.00, NULL, 10); INSERT INTO `emp` VALUES (7844, 'TURNER', 'SALESMAN', 7698, '1981-09-08', 1500.00, 0.00, 30); INSERT INTO `emp` VALUES (7876, 'ADAMS', 'CLERK', 7788, '1987-05-23', 1100.00, NULL, 20); INSERT INTO `emp` VALUES (7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950.00, NULL, 30); INSERT INTO `emp` VALUES (7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000.00, NULL, 20); INSERT INTO `emp` VALUES (7934, 'MILLER', 'CLERK', 7782, '1982-02-23', 1300.00, NULL, 10); INSERT INTO `emp` VALUES (8888, 'CHB', 'CLERK', 7369, '2018-12-10', 8000.00, 100.00, NULL); SET FOREIGN_KEY_CHECKS = 1;emp表SQL
dept表:
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for dept -- ---------------------------- DROP TABLE IF EXISTS `dept`; CREATE TABLE `dept` ( `deptno` int(2) NOT NULL, `dname` varchar(14) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL, `loc` varchar(13) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL, PRIMARY KEY (`deptno`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of dept -- ---------------------------- INSERT INTO `dept` VALUES (10, 'ACCOUNTING', 'NEW YORK'); INSERT INTO `dept` VALUES (20, 'RESEARCH', 'DALLAS'); INSERT INTO `dept` VALUES (30, 'SALES', 'CHICAGO'); INSERT INTO `dept` VALUES (40, 'OPERATIONS', 'BOSTON'); SET FOREIGN_KEY_CHECKS = 1;dept表SQL
建好表後,我們來創建一個存儲過程。
示例1:通過存儲過程完成查詢每個員工編號(empno)、姓名(ename)、職位(job)、領導編號(mgr)、領導姓名(empno)、部門名稱(dname)、部門位置(loc)。
delimiter // create procedure select_pro() begin select e1.empno bianhao, e1.ename xingming, e1.job zhiwei, e1.mgr lindaobianhao, e2.ename lindaoxingming, d.dname bumenmingchen,
d.loc bumenweizhi from emp e1 , emp e2 , dept d where e1.mgr=e2.empno and e1.deptno=d.deptno ; end // delimiter ;
註:“delimiter //”語句的作用是將MySQL的結束符設置為//,因為MySQL預設的語句結束符是分號“;”,為了避免與存儲過程中的SQL語句結束符相衝突,需要使用delimiter改變存儲過程的結束符,設置為以“end //”結束存儲過程。存儲過程定義完畢之後,再使用“delimiter;”回覆預設結束符。delimiter也可以指定其他符號作為結束符(“\”除外,這是轉義字元)。當然,如果你在Navicat等圖形界面下進行,可以不用設置delimiter。
示例1中SQL語句創建了一個名為select_pro的存儲過程,通過“call select_pro()”,即可完成查詢功能,不在需要每次查詢都重寫查詢語句。
示例2:創建一個帶參數的存儲過程,刪除emp表中empno為指定值得記錄,並返回最高最高月薪,也返回大於指定月薪的人數。
delimiter // create procedure param_pro(in id int , out num int, inout p_sal int) begin delete from emp where empno = id ; select max(sal) from emp into num; select count(*) into p_sal from emp where sal >P_sal ; end // delimiter ;
調用上面創建好的存儲過程param_pro:
set @p_sal = 1250 ; call param_pro(7369 , @num , @p_sal); select @num , @p_sal ;
輸出結果如下:
查看emp表,也發現empno為7369的記錄確實被刪除。
將查詢結果賦值給變數時,可以使用into關鍵字,既可以在select子句末尾寫into關鍵字,也可以在值後面寫into語句。
3.2 創建存儲函數
語法結構如下:
CREATE FUNCTION 函數名([ 參數名 數據類型 [, …]]) RETURNS返回類型 BEGIN 過程體 END
存儲過程與存儲函數一個很大的不同就是制定參數IN、OUT、INOUT只對存儲過程有用,存儲函數預設IN類型參數,不能設置其他兩種類型。RETURNS子句聲明返回值類型也只能在存儲函數中使用,且一個存儲函數必須包含一個RETURNS 語句。
示例3:用存儲函數查詢指定empno的員工的月薪sal
delimiter // create function fun1(id int) returns int begin return (select sal from emp where empno=id); end // delimiter ;
調用存儲函數fun1:
select fun1(7698)
輸出結果如下:
3.3 修改存儲過程和函數
使用ALTER語句可以修改存儲過程和函數的特性。語法結構如下:
ALTER {PROCEDURE | FUNCTION} sp_name [characteristic …]
其中,sp_name表示存儲過程或函數的名稱,characteristic參數指定存儲過程或函數的特性,可能取值有:
CONTAINS SQL:子程式包含SQL語句,但不包含讀或寫數據的語句。
NO SQL:子程式不包含SQL語句。
READS SQL DATA:子程式包含讀數據的語句。
MODIFIES SQL DATA:子程式包含寫數據的語句。
SQL SECURITY { DEFINER | INVOKER}:指明誰有許可權執行。
DEFINER:只有定義者自己才能執行。
INVOKER:調用者可以執行。
COMMENT ‘string’ :註釋。
示例4:示例1中創建的存儲過程param_pro,將其讀寫許可權該為MODIFIES SQL DATA,並指明調用者可以執行。
ALTER PROCEDURE param_pro MODIFIES SQL DATA SQL SECURITY INVOKER ;
4 流程式控制制語句
MySQL中用來構造流程式控制制語句的有:IF語句、CASE語句、LOOP語句、LEAVE語句、ITERATE語句、REPEAT語句和WHILE語句。每一個流程中可能包含一個單獨的語句,或者是使用BEGIN…END構造複雜語句,構造可以被嵌套。
(1)IF語句
IF語句包含多個條件判斷,根據判斷結果為TRUE或FALSE來執行相應的語句,語法格式如下:
IF expr_condition THEN statement_list [ELSEIF expr_condition THEN statement_list] [ELSE statement_list] END IF
註意:所以IF語句都需要用END IF來結束,在THEN中執行,ELSEIF和ELSE是可選的。
示例5:有一個變數val,判斷變數值是否為空,若為空,輸出“val is NULL”;否則輸出“val is not NULL”。
IF val IS NULL THEN SELECT ‘val is NULL’ ; ELSE SELECT ‘val is not NULL’ ; END IF;
(2)CASE語句
CASE是另一種條件判斷語句,該語句有兩種格式,第一種格式如下:
CASE case_expr WHEN when_value THEN statement_list [WHEN when_value THEN statement_list]…… [ELSE statement_list] END CASE
參數說明:
case_expr,表示條件判斷的表達式,決定了哪一個WHEN自己會被執行
When_value,表示表達式可能的值,如果,某個when_value表達式與case_expr表達式結果相同,則執行對應THEN關鍵字後的statement中的語句
Statement_list,表示不同when_value值的執行語句
示例6:使用CASE流程式控制制語句的第一種格式,判斷val值,若等於1則輸出‘val is 1’ , 若等於2則輸出‘val is 2’,或者兩者都不等於則輸出‘val is not 1 or 2’:
CASE val WHEN 1 THEN SELECT ‘val is 1’; WHEN 2 THEN SELECT ‘val is 2’; ELSE SELECT ‘val is not 1 or 2’; END CASE;
CASE語句的第二種格式:
CASE WHEN expr_condition THEN statement_list [WHEN expr_condition THEN statement_list] [ELSE statement_list] END CASE;
示例7:使用CASE流程式控制制語句的第二種格式判斷變數val是否為空,小於零、大於零、等於零,並作對應的輸出:
CASE WHEN val is NULL THEN SELECT ‘val is NULL’ ; WHEN val < 0 THEN SELECT ‘val is less than 0’ ; WHEN val > 0 THEN SELECT ‘val is greater than 0’ ; ELSE SELECT ‘val is 0’ ; END CASE ;
註意,這裡存儲過程中的CASE語句,與控制流程函數中的SQL CASE表達式中的CASE是不同的,存儲過程中,CASE語句不能有ELSE NULL子句,並且用END CASE代替END來終止。
(3) LOOP語句與LEAVE語句
LOOP語句迴圈語句用來重覆執行某些語句,與IF和CASE語句相比,LOOP只是創建了一個迴圈操作過程,並不進行條件判斷。LOOP內的語句一直被重覆執行直到迴圈被退出,跳出迴圈使用的是LEAVE子句,LOOP語句基本語法結構如下:
[loop_label:] LOOP statement_list END LOOP [lop_label]
loop_label表示LOOP語句的標註名稱,該參數可以省略。statement_list參數表示迴圈執行的語句。
示例8:定義一個變數id,初始值為0,迴圈執行id加1的操作 ,當id值小於10時,迴圈重覆執行,當id值大於或者等於10時,使用LEAVE語句退出迴圈
DECLARE id INT DEFAULT 0; Add_loop:LOOP SET id=id+1; IF id>=10 THEN LEAVE add_loop; END IF; END LOOP add_loop;
(4)ITERATE語句
ITERATE語句用於將執行順序轉到語句段的開頭處,語法格式如下:
ITERATE lable
其中,lable,表示迴圈的標誌.註意,ITERATE語句只可以出現在,LOOP、REPEAT和WHILE語句中。ITERATE的作用類似於Java和Python中的continue關鍵字。
示例9:p1的初始值為0,如果,p1的值小於10時,重覆執行p1加1的操作,當p1大於或等於10,並且小於20時,列印消息p1 is between 10 and 20,當p1大於20時,退出迴圈
演示ITERATE語句,在LOOP語句內的使用
CREATE PROCEDURE doiterate() BEGIN DECLARE p1 INT DEFAULT 0; my_loop:LOOP SET p1=p1+1; IF p1<10 THEN ITERATE my_loop; ELSEIF p1>20 THEN LEAVE my_loop; END IF; SELECT ‘p1 is between 10 and 20’; END LOOP my_loop; END
(5)REPEAT語句
REPEAT語句用於創建一個帶有條件判斷的迴圈過程,每次語句執行完畢之後,會對條件表達式進行判斷,如果表達式為真,則迴圈結束,否則,重覆執行迴圈中的語句。語法結構如下:
[repeat_lable:] REPEAT statement_list UNTIL expr_condition END REPEAT [repeat_lable]
其中,repeat_lable,為REPEAT語句的標註名稱,該參數是可選的,REPEAT語句內的語句,或語句群被重覆,直至expr_condition為真。
示例10:id值小於10前,重覆迴圈讓id值加1,使用REPEAT語句,執行迴圈過程
DECLARE id INT DEFAULT 0; REPEAT SET id=id+1; UNTIL id>=10; END REPEAT;
(6)WHILE語句
WHILE語句創建一個帶條件判斷的迴圈過程 與REPEAT不同的是,WHILE在語句執行時,先對指定的條件進行判斷,如果為真,則執行迴圈內的語句,否則退出迴圈。語法結構如下:
[while_lable:] WHILE expr_condition DO Statement_list END WHILE [while_lable]
其中,while_lable為WHILE語句的標註名稱,Expr_condition,為進行判斷的表達式,如果表達式為真,WHILE語句內的語句,或語句群就被執行,直至expr_condition為假,退出迴圈。
示例11:創建一個變數i,初始值為0,當i小於10時重覆執行加1。
DECLARE i INT DEFAULT 0; WHILE i<10 DO SET i=i+1; END WHILE;
5 查看存儲過程和函數
(1) 使用SHOW STATUS語句查看存儲過程和函數的狀態
SHOW STATUS語句可以查看存儲過程和函數的狀態,其基本語法結構如下:
SHOW {PROCEDURE | FUNCTION} STATUS [LIKE ‘pattern’]
語法結構中,使用LIKE語句表示匹配存儲過程或函數的名稱。
示例12:查看示例2中創建的存儲過程信息。
SHOW PROCEDURE STATUS LIKE ‘param_pro’ ;
部分輸出結果如下:
(2)使用SHOW CREATE語句查看存儲過程和函數的定義
SHOW CREATE語法結構如下:
SHOW CREATE {PROCEDURE | FUNCTION} sp_name
示例13:查看示例3中創建的存儲函數信息。
SHOW CREATE FUNCTION fun1;
(3)從information_schema.Routines表中查看存儲過程和函數信息
MySQL中的存儲過程和函數的信息存儲在information_schema.Routines表中,可以通過查詢該表中的記錄來查詢存儲過程和函數的信息。
示例14:從Routines表中查看形成為param_pro的存儲過程信息。
SELECT * FROM information_schema.Routines WHERE ROUTINE_NAME = 'param_pro' AND ROUTINE_TYPE='PROCEDURE' ;
查詢結果如下:
6 刪除存儲過程和函數
刪除存儲過程和函數可以使用DROP語句,其語法結構如下:
DROP {PROCEDURE | FUNCTION } [IF EXISTS] sp_name
示例15:刪除存儲過程select_pro和存儲函數fun1。
DROP PROCEDURE IF EXISTS select_pro ; DROP FUNCTION IF EXISTS fun1 ;
7 總結
本文系統地介紹了MySQL中存儲過程和存儲函數的使用,包括了存儲過程和存儲函數的創建、修改、查看、刪除等內容。不過對於游標等內容並未介紹。