MySQL數據庫之存儲過程與存儲函數


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中存儲過程和存儲函數的使用,包括了存儲過程和存儲函數的創建、修改、查看、刪除等內容。不過對於游標等內容並未介紹。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM