存儲過程與存儲函數


【應知部分】:

1.存儲過程與存儲函數概述

  存儲過程與存儲函數是MySQL自5.0版本之后開始支持的過程式數據庫對象。它們作為數據庫存儲的重要功能,可以提高數據庫的處理速度,同時也可以提高數據庫編程的靈活性。

 

2. 存儲過程的概念

   概念:

   存儲過程是一組為了完成某特定功能的SQL語句集,其實質上就是一段存放在數據庫中的代碼,它可以由聲明式語句(如CREATE、UPDATE和SELECT等語句)和過程式語句(如IF-THEN-ELSE控制結構語句)組成。這組語句經過編譯后會存儲在數據庫中,用戶只需通過指定存儲過程的名字並給定參數(如果該存儲過程帶有參數),即可隨時調用處理它,而不必重新編譯,提高數據庫操作語句的執行效率。

 

   優點:

(1)增強SQL語言的功能與靈活性。

(2)良好的封裝性。

(3)高性能。

(4)可減少網絡流量。

(5)保證數據庫的安全性和數據的完整性。

 

3. 存儲過程體的作用

   在存儲過程體中可以使用各種SQL語句與過程式語句的組合,來封裝數據庫應用中復雜的業務邏輯和處理規則,以實現數據庫應用的靈活編程。

   構造存儲過程體的語法元素:

(1)局部變量

(2)SET語句

(3)SELECT…INTO語句

(4)流程控制語句

(5)游標

 

4. 存儲函數的概念

   在MySQL中,存在一種與存儲過程十分相似的過程式數據庫對象——存儲函數。它與存儲過程一樣,都是由SQL語句和過程式語句組成的代碼片段,並且可以被應用程序和其他SQL語句調用。

   存儲函數與存儲過程的區別:

(1)存儲函數不能擁有輸出參數。存儲過程擁有輸出參數。

(2)可以直接對存儲函數調用,不需要使用CALL語句,存儲過程調用需要。

(3)存儲函數中必須包含RETURN語句,此語句不允許出現在存儲過程中。

 

【應會部分】:

一、 創建存儲過程

語法:CREATE PROCEDURE sp_name([proc_parameter[,…]])

      [characteristic…]routine_body

其中,proc_parameter的格式為:

      [IN|OUT|INOUT]param_name type

      type的格式為:

      

Any valid MySQL data type

      characteristic的格式為:

      COMMENT ‘string’

      |LANGUAGE SQL

      |[NOT]DETERMINISTIC

      |{ CONTAINS SQL|NO SQL|READES SQL DATA|MODIFIES SQL DATA}

      |SQL SECURITY{DEFINER|INVOKER}

 

      routine_body的格式為:

      

Valid SQL routine statement

 

主要語法說明:(詳細說明見P141

★sp_name:存儲過程的名稱,默認在當前數據庫下創建。定義名字應該避免與MySQL內置函數相同。

★proc_parameter:存儲過程參數列表。參數取名不要與數據表的列名相同。

characteristic:存儲過程的某些特征設定。

routine_body:存儲過程的主體部分,也稱為存儲過程體,其包含了在過程調用的時候必須執行的SQL語句。這個部分以BEGIN開始,以關鍵字END結束。

DELIMITER命令的語法格式:

  DELIMITER $$(略)

例題:

1)將MySQL結束符修改為兩個感嘆號“$$”

修改:mysql> delimiter $$

還原:mysql> delimiter ;

 

2)在數據庫db_school中創建一個存儲過程,用於實現給定表tb_student中一個學生的學號即可修改表tb_student中該學生的性別為一個指定的性別(使用該存儲過程修改學生表中的學號、性別)。

mysql> use db_school;
mysql> delimiter $$
mysql> create procedure sp_update_sex(in sno int,in ssex nchar(2))

begin
update tb_student set sex=ssex where studentno=sno;
end $$

mysql> delimiter ;

 

注意:“ssex nchar(2)” 在Linux系統下的Mysql數據庫管理系統中,由於字符集問題導致亂碼的情況時有發生,大多是因為兼容性造成的,我們這里使用的是Unicode編碼,能夠表示全世界所有的字節。使用存儲過程插入或更新的字符有可能出現亂碼問題,為保證不出現亂碼我們這里采用Unicode編碼,即nchar或nvarchar。

查看存儲過程狀態你就會明白:

mysql>show procedure status\G

*************************** 1. row ***************************

                  Db: db_school

                Name: sp_update_sex

                Type: PROCEDURE

             Definer: root@localhost

            Modified: 2018-05-19 21:38:52

             Created: 2018-05-19 21:38:52

       Security_type: DEFINER

             Comment:

mysql>character_set_client: utf8

collation_connection: utf8_general_ci

Database Collation: latin1_swedish_ci

1 row in set (0.00 sec)

 

或查看指定的數據庫“存儲過程”。

 

show create procedure sp_update_sex\G

*************************** 1. row ***************************

           Procedure: sp_update_sex

            sql_mode:

    Create Procedure: CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_update_sex`(in sno int,in ssex nchar(1))

begin

update tb_student set sex=ssex where studentno=sno;

end

character_set_client: utf8

collation_connection: utf8_general_ci

Database Collation: latin1_swedish_ci

1 row in set (0.00 sec)

 

另外需要注意的是:如果你不是使用root用戶創建的數據庫存儲過程,一定要去查看它是否有create routine權限。否則儲存過程是無法創建的。

 

查看方法:以用戶’root’@’localhost’為例

mysql> select Create_routine_priv from mysql.user

    -> where user='root' and host='localhost';

+-----------------------------------+

| Create_routine_priv     |

+-----------------------------------+

| Y                     |

+------------------------------------+

1 row in set (0.00 sec)

 

‘Y’說明擁有該權限。

 

【調用】儲存過程怎樣使用,我們這里使用以上創建的儲存過程來驗證。

mysql> select studentNo,sex

    -> from tb_student

    -> where studentNo='2013110101';

+------------------+---------+

| studentNo  | sex   |

+------------------+---------+

| 2013110101 ||

+------------------+---------+

 

1 row in set (0.00 sec)這里我們使用存儲過程把學號為“2013110101”的同學的性別更新為“女”。

mysql> call sp_update_sex('2013110101',n'');

驗證:

mysql> select studentNo,sex

      from tb_student

      where studentNo='2013110101';

+-------------------+----------+

| studentNo   | sex   |

+-------------------+----------+

| 2013110101  ||

+-------------------+----------+

1 row in set (0.00 sec)

 

驗證成功!

 

授予普通用戶擁有“存儲過程或函數”的相關權限:(復習授權)

User表中的列

權限名稱

權限的范圍

Alter_routine_priv

ALTER ROUTINE

修改存儲過程和函數

Create_routine_priv

CREATE ROUTINE

創建存儲過程和函數

Execute_priv

EXECUTE

執行存儲過程和函數

mysql> use mysql

mysql> grant ALTER ROUTINE,CREATE ROUTINE,EXECUTE on `db_school`.*

to 'bob'@'localhost' identified by '123456';

 

或者:

mysql> use mysql


mysql> create user 'bob1'@'localhosat' identified by '123456';


mysql>update user

set Create_routine_priv='Y',

Alter_routine_priv='Y',

Execute_priv:='Y'

where user='bob1' and host='localhost';

 

其它權限如果需要也必須開啟!

二、存儲過程

1)局部變量

作用:在存儲過程體中可以設置局部變量,用來存儲存儲過程體中的臨時結果。在mysql5.5中可以使用DECLARE語句來聲明局部變量並同時還可以對該局部變量賦予一個初始值。

其語法格式為:

DECLARE var_name[ ,…] type [DEFAULT value]

語法說明:

var_name:用於指定局部變量的名稱

type:用於聲明局部變量的數據類型

DEFAULT子句:用於為局部變量指定一個默認值。若沒有指定默認為空。

注意:

(1)局部變量只能在存儲過程體的BEGIN…END語句塊中聲明。

(2)局部變量必須在存儲過程體的開頭處聲明。

(3)局部變量的作用范圍僅限於聲明它的BEGIN…END語句塊,其它語句塊中的語句不可以使用它。

(4)局部變量不同於用戶變量,兩者的區別是:局部變量聲明時,在其前面沒有使用“@”符號,並且它只能聲明它的BEGIN…END語句塊中的語句所使用;用戶變量在聲明時,會在前面使用“@”符號,同時聲明的用戶變量存在於整個會話之中。

2)SET語句

在MySQL5.5中可以使用SET語句為局部變量賦值,其語法格式是:

SET var_name=expr[,var_name=expr];

例題:使用存儲過程計算兩個值的和

 

mysql>use db_school   #存儲過程是數據庫對象,因此必須選擇一個數據庫

mysql> delimiter $$

mysql>create procedure sp_sum(in a int,in b int)

begin

declare c int;

set c=a+b;

select c;

end $$

mysql> delimiter ;

mysql> call sp_sum(5,6);

+---- -------+

| c      |

+-----------+

|   11  |

+-----------+

1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

 

3)SELECT …INTO語句

   在MySQL中,可以使用SELECT…INTO語句把選定列的值直接存儲到局部變量中,其語法格式是:

SELECT col_name[,…]INTO var_name[,…]table_expr

語法說明如下:

★col_name:用於指定列名。

★var_name:用於指定要賦值的變量名。

★table_expr:表示使用SELECT語句中的FROM子句及后面的語法部分。

注意:存儲過程體中的SELECT…INTO語句返回的結果集只能有一行數據。

 

例題:創建一個存儲過程,用於得到某個學生的籍貫。

mysql> delimiter $$

mysql> create procedure sp_native1(in sno int)

      begin

      declare na nchar(20);

      select native into na from tb_student

      where studentno=sno;

      select na;

      end$$       

mysql> delimiter ;

 

調用:

mysql> call sp_native1 ('2013110201');

+----------------+

| na        |

+----------------+

| 內蒙古   |

+----------------+

1 row in set (0.00 sec)

 

Query OK, 0 rows affected (0.00 sec)

 

例題:創建一個存儲過程,用於得到某個指定學生的籍貫。

mysql> delimiter $$

mysql> create procedure sp_native2(inout sno int)

      begin

      declare na nchar(20);

      select native into na from tb_student

      where studentno=sno;

      select na;

      end$$    

mysql> delimiter ;

 

注意:調用前這里必須先賦值。

mysql> set @sno=2013110101;

mysql> call sp_native2 (@sno);

+-------------+

| na     |

+-------------+

| 山西   |

+-------------+

1 row in set (0.01 sec)

 

Query OK, 0 rows affected (0.01 sec)

 

【理解】:參數類型in、out、inout的區別!具體內容請參閱P141。

 

4)流程控制語句

   在MySQL 5.5中,可以在存儲過程體中使用以下兩類用於控制語句流程的過程式SQL語句。

(1)條件判斷語句

   

常用的條件判斷語句有IF-THEN-ELSE語句和CASE語句。其中,IF-THEN-ELSE語句可以根據不同的條件執行不同的操作,其語法格式為:

     IF search_condition THEN statement_list

         [ELESEIF search_condition THEN statement_list][ELSE statement_list]

     END IF

 

語法及使用說明如下:

★search_condition:用於指定判斷條件。

★statement_list:用於表示包含了一條或多條的SQL語句。

★只有當判斷search_condition為真時,才會執行相應的SQL語句。

★IF-THEN-ELSE語句不同於系統函數IF()。

 

     CASE語句在存儲過程中的使用具有兩種語法格式,分別是:

     CASE case_value

          WHEN when_value THEN statement_list

          [WHEN when_value THEN statement_list][ELSE statement_list]

     END CASECASE

         WHEN search_condition THEN statement_list

         [WHEN search_condition THEN statement_list]

         [ELSE statement_list]

     END CASE

 

語法說明如下:

★第一種語法格式中的case_value用於指定要判斷的值或表達式,隨后緊跟的是一系列WHEN-THEN語句塊。其中,第一個WHEN-THEN語句塊中的參數when_value用於指定要與case_value進行比較的值。倘若比較的值為真,則執行對應的statement_list中的SQL語句。如若每一個WHEN-THEN語句塊中的參數when_value都不能與case_value相匹配,則會執行ELSE子句中指定的語句。該CASE語句最終會以關鍵字END CASE作為結束。

 

★第二種語法格式中的關鍵字CASE后面沒有指定參數,而是在WHEN-THEN語句塊中使用search_condition指定一個比較表達式。若表達式為真,則會執行對應的關鍵字THEN后面的語句。與第一種語法格式相比,這種語法格式能夠實現更為復雜的條件判斷,而且使用起來會更方便。

 

例題:新建存儲過程用於實現插入不同的數據(1)。

use db_school

Create table table1(variable1 char(10));

 

DELIMITER $$

CREATE PROCEDURE proc1

(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 $$

DELIMITER ;

 

 

驗證:

set @parameter1=18;

call proc1(@parameter1);

select * from table1;

 

例題:新建存儲過程用於實現插入不同的數據(2)

 

Create table t(s1 int);

 

DELIMITER $$

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 $$ 

DELIMITER ;

 

驗證:

Set @parameter=-1;

Call proc2(@parameter);

Select * from t;
Set @parameter=0;

Call proc2(@parameter);

Select * from t;

 

 

 

例題:新建存儲過程用於實現插入不同的數據(3)CASE語句

Create table t1(s1 int);

 

DELIMITER $$ 

CREATE PROCEDURE proc3(in parameter int)

begin

declare var int; 

set var=parameter+1; 

case var 

when 0 then insert into t1 values(17); 

when 1 then insert into t1 values(18); 

else insert into t1 values(19); 

end case; 

end $$

DELIMITER ;

 

驗證

set @parameter=0;

call proc3(@parameter);

select * from t1;

或者

DELIMITER  $$ 

CREATE PROCEDURE proc4(in parameter int) 

begin

declare var int; 

set var=parameter+1; 

case 

when var=0 then

insert into t1 values(30);

when var>0 then

insert into t1 values(40);

when var<0 then

insert into t1 values(50);

else

insert into t1 values(60);

end case;

end $$

DELIMITER ;

 

 

驗證:

set @parameter=-1;

call proc3(@parameter);

select * from t1;

(2)循環語句

     常用的循環語句有WHILE語句、REPEAT語句和LOOP語句。

WHILE語句語法格式如下:

while條件 do

--循環體

End while

 

例題:新建存儲過程用於實現插入不同的數據(4)-WHILE語句。

DELIMITER  $$

CREATE PROCEDURE proc5() 

begin

declare var int; 

set var=0; 

while var<6 do 

insert into t values(var); 

set var=var+1; 

end while; 

end $$

DELIMITER ;

 

驗證:

delete from t;

call proc5;

select * from t;

 

 

REPEAT語句語法格式如下:

repeat

--循環體

until循環條件    

endrepeat;

注:它在執行操作后檢查結果,而while則是執行前進行檢查。

 

例題:新建存儲過程用於實現插入不同的數據(5)-REPEAT語句。

DELIMITER $$ 

CREATE PROCEDURE proc6() 

begin  

declare v int; 

set v=0; 

repeat 

insert into t values(v); 

set v=v+1; 

until v>=5 

end repeat; 

end $$

DELIMITER ;

 

驗證:

delete from t;

call proc6;

select * from t;

 

LOOP語句:

注:loop循環不需要初始條件,這點和while 循環相似,同時和repeat循環一樣不需要結束條件, leave語句的意義是離開循環;

例題:新建存儲過程用於實現插入不同的數據(6)-LOOP語句。

DELIMITER $$ 

CREATE PROCEDURE proc7() 

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 $$

DELIMITER ;

 

 

驗證:

delete from t;

call proc7;

select * from t;

 

LABLES:標號:

標號可以用在begin repeat while 或者loop 語句前,語句標號只能在合法的語句前面使用。可以跳出循環,使運行指令達到復合語句的最后一步;

 

ITERATE(迭代)語句:

通過引用復合語句的標號,來從新開始復合語句。

例題:新建存儲過程用於實現插入不同的數據(7)- ITERATE(迭代)語句。

 

DELIMITER $$ 

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 $$

DELIMITER ;

 

驗證:

 

delete from t;

call proc10;

select * from t;

 

MySQL存儲過程的查詢:

1select name from mysql.proc where db=’數據庫名’;

(2) show procedure status where db=’數據庫名’;

(3) SHOW CREATE PROCEDURE 數據庫.存儲過程名 \G

 

5)游標

   游標(cursor)就是游動的標識,通俗的這么說,一條sql取出對應n條結果資源的接口/句柄,就是游標,沿着游標可以一次取出一行;

 

 

使用場景:

1.只能用於存儲過程或存儲函數中,不能單獨在查詢操作中使用;

2.在存儲過程或存儲函數中可以定義多個游標,但是一個BEGIN...END

語句塊中每一個游標的名字必須是唯一的;

3.游標不是一條SELECT語句,是被SELECT語句檢索出來的結果集;

 

游標使用步驟:

1)聲明游標:使用游標前,必須申明(定義)它,定義要使用的SELECT語句;

 

DECLARE 游標名稱 CURSOR FOR SELECT語句(返回一行或多行的數據);

 

2)打開游標:使用游標前,打開游標

OPEN 游標名稱;

 

3)讀取數據:對於填有數據的游標,可根據需要取出數據;

FETCH 游標名 INTO 變量名1,...變量名n;

注:FETCH語句是將游標指向的一行數據賦給一些變量,這些變量的數目必須等於聲明游標SELECT子句中選擇列的數目,游標相當於一個指針,指向當前的一行數據;

  1. 關閉游標:CLOSE 游標名稱

例題:在數據庫db_school中創建一個存儲過程,用於計算表tb_student中數據行的行數。

USE db_school;

Delimiter $$

Create procedure sp_sumrow(in rows int)

Begin

DECLARE sno CHAR;

DECLARE FOUND BOOLEAN DEFAULT TRUE;

DECLARE cur CURSOR FOR SELECT studentno from tb_student;

DECLARE CONTINUE HANDLER FOR NOT FOUND

SET FOUND=FALSE;

SET rows=0;

OPEN cur;

FETCH cur INTO sno;

WHILE FOUND DO

SET rows=rows+1;

FETCH cur INTO sno;

END WHILE;

CLOSE cur;

Select @rows;

End $$

Delimiter ;

 

 

驗證:

Call sp_sumrow(@rows);

 

6)刪除存儲過程

mysql>drop procedure sp_sumrow;

 

三、存儲函數

create function 存儲函數名(參數 數據類型)

RETURES 數據類型

 

存儲函數體

 

Return values 返回數值給存儲函數體

 

語法:

 

CREATE FUNCTION sp_name([func_parameter[,...]])

RETURNS type

[characteristic ...] routine_body

Return

 

存儲函數語法:

Create function 函數([函數參數[,….]])

Returns 返回類型

Begin

If(

Return (返回的數據)

Else

Return(返回的數據)

end if;

end;

 

例1:在數據庫db_school中創建一個存儲函數,要求該函數根據給定的學號返回學生的姓名;

-- 創建存儲函數

 

DELIMITER  $$

CREATE FUNCTION  f_studentname(id INT )

RETURNS CHAR(80) 

Begin

RETURN (SELECT Studentname FROM tb_student  WHERE studentno=id );

END

$$

DELIMITER ;

Select f_studentname(‘2013110101’);

 

例2:在數據庫db_school中出創建一個存儲函數,要求該函數根據給定的學號返回學生的性別,

如果數據庫中沒有給定的學號,則返回‘沒有該學生’;

DELIMITER  $$

CREATE FUNCTION search_sex1(sno char(10))

Returns char(2)

deterministic

Begin

Declare ssex char(2);

Select sex INTO ssex from tb_student where studentno=sno;

IF SSEX IS NULL THEN

RETURN(SELECT '沒有該學生');

ELSE IF SSEX='' THEN

Return(Select '');

ELSE

Return(Select '');

END IF;

END IF;

end $$

Select search_sex1(‘2013110101’)$$

 

 

查看存儲過程和函數

 

存儲過程和函數創建以后,可以查看存儲過程和函數的狀態和定義。

 

通過SHOW STATUS語句來查看存儲過程和函數的狀態,也可以通過SHOW CREATE語句來查看存儲過程和函數的定義。

 

通過查詢information_schema數據庫下的Routines表來查看存儲過程和函數的信息

 

1、SHOW STATUS語句查看存儲過程和函數的狀態

 

MySQL中可以通過SHOW STATUS語句查看存儲過程和函數的狀態。其基本語法形式如下:

 

SHOW { PROCEDURE | FUNCTION } STATUS [ LIKE  ' pattern ' ] ;

 

其中,PROCEDURE參數表示查詢存儲過程;FUNCTION參數表示查詢存儲函數;

 

LIKE ' pattern '參數用來匹配存儲過程或函數的名稱。

SHOW  FUNCTION STATUS LIKE '% search_sex1%';

 


免責聲明!

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



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