MySQL全面瓦解16:存儲過程相關


概述

大多數SQL語句都是針對一個或多個表的單條語句。但並非所有業務都這么簡單,經常會有復雜的操作需要多條語句才能完成。

比如用戶購買一個商品,要刪減庫存表,要生成訂單數據,要保存支付信息等等,他是一個批量的語句執行行為。

存儲過程簡單來說,就是為以后的使用而保存的一條或多條MySQL語句的集合。可將其視為批文件,雖然它們的作用不僅限於批處理。 

優點:
提高代碼的復用性:把一些通用操作內容封裝到一個存儲過程中,可以不斷的給業務功能復用。

簡化操作:避免在業務中寫大量的代碼

提高效率:減少執行次數和數據庫服務器連接次數。

提高安全性:通過存儲過可以減少對基礎數據的誤操作,參數化的存儲過程一定程度上可以防止SQL注入式攻擊,而且可以將Grant、Deny以及Revoke權限應用於存儲過程。 

說存儲過程之前,先來了解兩個重要的知識點:自定義變量 和 delimiter關鍵字。

自定義變量

概念

變量由用戶自定義的,而非系統已經存在的。

使用步驟

第一步聲明;第二步賦值;第三步使用(調用、比較和運算)

分類

包含用戶變量和局部變量,我們一個個來看:

用戶變量

作用域

針對當前會話有效,作用域同會話變量。

用戶變量可以在任何地方使用,既可以是包含的begin和end,也可以是在這之外。

使用

聲明並初始化
1 set @variable=value;
2 or
3 set @variable:=value;
4 or
5 select @variable:=value;

這邊需要注意:使用了@符號來定義 變量,set中=號前面冒號是可選的,select方式=前面必須有冒號。

賦值方式

一種方式就是跟聲明並初始化一致,直接set、select進行賦值,

另外一種就是直接從其他表、視圖或變量中查詢並賦值,如下:

1 select columnname into @variable from tname; 

這邊需要注意兩種select的使用方式

實踐一下

 1 mysql> set @var1='num1';
 2 set @var2:='num2';
 3 select @var3:='num3';
 4 select @var1,@var2,@var3;
 5 Query OK, 0 rows affected
 6 
 7 Query OK, 0 rows affected
 8 
 9 +---------------+
10 | @var3:='num3' |
11 +---------------+
12 | num3          |
13 +---------------+
14 1 row in set
15 
16 +-------+-------+-------+
17 | @var1 | @var2 | @var3 |
18 +-------+-------+-------+
19 | num1  | num2  | num3  |
20 +-------+-------+-------+
21 1 row in set

局部變量

作用域

declare用於定義局部變量,在存儲過程和函數中通過declare定義變量在begin…end中,且在語句之前。並且可以通過重復定義多個變量

declare變量的作用范圍同編程里面類似,在這里一般是在對應的begin和end之間。在end之后這個變量就沒有作用了,不能使用了。這個同編程一樣。

使用

聲明語法
1 declare variable type [default default_value]

declare 變量名 變量類型,后面的 [ 默認值] 為可選;

賦值方式
1 set variable=value;
2 set variable:=value;
3 select variable:=value;
4 
5 或者
6 
7 select cname into variable from tname; 

注意自定義變量和局部變量的區別,一個前面有@符號,一個沒有。

查看變量的值

1 select variable

實踐一下

 1 mysql> 
 2 /*這邊聲明腳本的結束符為// */
 3 DELIMITER //
 4 DROP PROCEDURE IF EXISTS sp_avg;
 5 CREATE PROCEDURE sp_avg()
 6 BEGIN
 7   /*聲明了一個局部變量 avg_score*/
 8   DECLARE avg_score int;
 9   select AVG(score) into avg_score from students;
10   select avg_score;
11   -- Todo
12 END //
13 /*重置腳本的結束符為; */
14 DELIMITER ;
15 Query OK, 0 rows affected
16 
17 mysql>
18 /*調用存儲過程*/
19 call sp_avg();
20 +-----------+
21 | avg_score |
22 +-----------+
23 |        87 |
24 +-----------+
25 1 row in set
26 
27 Query OK, 0 rows affected

 

 
        
 變量類型 作用域 定義位置 語法格式
用戶變量 當前會話都有效 會話的任一地方 @符號,無需指定類型
局部變量 所屬定義的begin end之間 begin...end中的第一個位置,緊跟在begin后面 不加@符號,需指定類型

 

delimiter 關鍵字的使用 

簡介

delimiter是mysql分隔符,在mysql客戶端中分隔符默認是分號;。如果一次輸入的語句較多,並且語句中間有分號,這時需要新指定一個特殊的分隔符。

其實就是告訴mysql解釋器,該段命令是否已經結束了,mysql是否可以執行了。默認情況下,delimiter是分號;。在命令行客戶端中,如果有一行命令以分號結束,那么回車后,mysql將會執行該命令。

詳細解釋: 

其實就是告訴mysql解釋器,該段命令是否已經結束了,mysql是否可以執行了。 

默認情況下,delimiter是分號;。在命令行客戶端中,如果有一行命令以分號結束, 那么回車后,mysql將會執行該命令。如輸入下面的語句 :

1 mysql> select * from tname;  

然后回車,那么MySQL將立即執行該語句。

使用

但有時候,不希望MySQL這么做。在為可能輸入較多的語句,且語句中包含有分號。 這種情況下,就需要事先把delimiter換成其它符號,如//、$$或者;;。

更改結束標志的定義如下:

1 mysql>delimiter // 

舉個例子:創建一個存儲過程,在創建該存儲過程之前,將delimiter分隔符轉換成符號“//”,最后在轉換回符號“;”。

1 /*將結束標志符更改為// */
2 delimiter //
3 /*創建函數或存儲過程*/
4 -- Todo,這邊寫下你的sql語句
5 end //
6 /*重置腳本的結束符為; */
7 delimiter ; 

上面就是,先將分隔符設置為 //, 直到遇到下一個 //,才整體執行語句。

執行完后,最后一行, delimiter ; 將mysql的分隔符重新設置為分號;

如果不修改的話,本次會話中的所有分隔符都以// 為准。 

存儲過程操作

存儲過程的操作包含創建

創建存儲過程

1 create procedure 存儲過程名([參數模式] 參數名 參數類型)
2 begin
3     存儲過程體
4 end

參數模式有3種:

in:該參數可以作為輸入,也就是該參數需要調用方傳入值。

out:該參數可以作為輸出,也就是說該參數可以作為返回值。

inout:該參數既可以作為輸入也可以作為輸出,也就是說該參數需要在調用的時候傳入值,又可以作為返回值。

參數模式默認為IN,一個存儲過程可以有多個輸入、多個輸出、多個輸入輸出參數。

所以創建存儲過程的時候參數可能存在一下幾種情況:

無參情況

編寫存儲過程

 1 /*設置結束符設置為// */
 2 DELIMITER //
 3 /*存儲過程如果存在先刪除*/
 4 DROP PROCEDURE IF EXISTS sp_test1;
 5 /*創建無參數存儲過程sp_test1*/
 6 CREATE PROCEDURE sp_test1()
 7 BEGIN
 8 update students set score = (score+1) where studentname='lala';
 9 END //
10 /*將結束符重新設置為;*/
11 DELIMITER; 

調用實現:對比數據可確定調用成功

 1 mysql> select *  from students;
 2 +-----------+-------------+-------+---------+
 3 | studentid | studentname | score | classid |
 4 +-----------+-------------+-------+---------+
 5 |         1 | brand       | 97.5  |       1 |
 6 |         2 | helen       | 96.5  |       1 |
 7 |         3 | lyn         | 96    |       1 |
 8 |         4 | sol         | 97    |       1 |
 9 |         7 | b1          | 81    |       2 |
10 |         8 | b2          | 82    |       2 |
11 |        13 | c1          | 71    |       3 |
12 |        14 | c2          | 72.5  |       3 |
13 |        19 | lala        | 53    |       0 |
14 +-----------+-------------+-------+---------+
15 9 rows in set
16 
17 mysql> call sp_test1();
18 Query OK, 1 row affected
19 
20 mysql> select *  from students;
21 +-----------+-------------+-------+---------+
22 | studentid | studentname | score | classid |
23 +-----------+-------------+-------+---------+
24 |         1 | brand       | 97.5  |       1 |
25 |         2 | helen       | 96.5  |       1 |
26 |         3 | lyn         | 96    |       1 |
27 |         4 | sol         | 97    |       1 |
28 |         7 | b1          | 81    |       2 |
29 |         8 | b2          | 82    |       2 |
30 |        13 | c1          | 71    |       3 |
31 |        14 | c2          | 72.5  |       3 |
32 |        19 | lala        | 54    |       0 |
33 +-----------+-------------+-------+---------+
34 9 rows in set
帶in參數

編寫存儲過程:

 1 /*設置結束符為// */
 2 DELIMITER //
 3 /*存儲過程如果存在先刪除*/
 4 DROP PROCEDURE IF EXISTS sp_test2;
 5 /*創建存儲過程sp_test2*/
 6 CREATE PROCEDURE sp_test2(sname varchar(20),score DECIMAL(10,2),classid int)
 7 BEGIN
 8 INSERT INTO students(studentname,score,classid) VALUES (sname,score,classid);
 9 END //
10 /*將結束符重新置為;*/
11 DELIMITER ; 

調用實現:

 1 mysql> set @uname='wzh1',@score=100,@classid=8;
 2 call sp_test2(@uname,@score,@classid);
 3 Query OK, 0 rows affected
 4 
 5 Query OK, 1 row affected
 6 
 7 mysql> select * from students;
 8 +-----------+-------------+-------+---------+
 9 | studentid | studentname | score | classid |
10 +-----------+-------------+-------+---------+
11 |         1 | brand       | 97.5  |       1 |
12 |         2 | helen       | 96.5  |       1 |
13 |         3 | lyn         | 96    |       1 |
14 |         4 | sol         | 97    |       1 |
15 |         7 | b1          | 81    |       2 |
16 |         8 | b2          | 82    |       2 |
17 |        13 | c1          | 71    |       3 |
18 |        14 | c2          | 72.5  |       3 |
19 |        19 | lala        | 54    |       0 |
20 |        20 | wzh1        | 100   |       8 |
21 +-----------+-------------+-------+---------+
22 10 rows in set 
帶out參數

編寫存儲過程

 1 /*設置結束符為// */
 2 DELIMITER //
 3 /*如果存儲過程存在則刪除*/
 4 DROP PROCEDURE IF EXISTS sp_test3;
 5 /*創建存儲過程sp_test2*/
 6 CREATE PROCEDURE sp_test3(sname varchar(20),score DECIMAL(10,2),classid int,out lastid int)
 7 BEGIN
 8 INSERT INTO students(studentname,score,classid) VALUES (sname,score,classid);
 9 select lastid = @@identity;
10 END //
11 /*將結束符重新置為;*/
12 DELIMITER ;

調用實現

 1 mysql> set @uname='wzh3',@score=104,@classid=10;
 2 call sp_test3(@uname,@score,@classid,@lastid);
 3 select @lastid;
 4 Query OK, 0 rows affected
 5 
 6 Query OK, 1 row affected
 7 
 8 +---------+
 9 | @lastid |
10 +---------+
11 |      22 |
12 +---------+
13 1 row in set
14 
15 mysql> select * from students;
16 +-----------+-------------+-------+---------+
17 | studentid | studentname | score | classid |
18 +-----------+-------------+-------+---------+
19 |         1 | brand       | 97.5  |       1 |
20 |         2 | helen       | 96.5  |       1 |
21 |         3 | lyn         | 96    |       1 |
22 |         4 | sol         | 97    |       1 |
23 |         7 | b1          | 81    |       2 |
24 |         8 | b2          | 82    |       2 |
25 |        13 | c1          | 71    |       3 |
26 |        14 | c2          | 72.5  |       3 |
27 |        19 | lala        | 54    |       0 |
28 |        20 | wzh1        | 100   |       8 |
29 |        21 | wzh2        | 101   |       9 |
30 |        22 | wzh3        | 104   |      10 |
31 +-----------+-------------+-------+---------+
32 12 rows in set 
帶inout參數

自己試試吧,小伙子們

調用存儲過程

1 call 存儲過程名稱(參數列表);

注意:調用存儲過程關鍵字是call

如上所示 ,所有的call都是這樣的額

刪除存儲過程

1 drop procedure [if exists] 存儲過程名稱;

 存儲過程只能一個個刪除,不能批量刪除。

if exists:表示存儲過程存在的情況下刪除,我們上面演示的存儲過程都是判斷如果存在就先刪除。

修改存儲過程

存儲過程不能修改,若涉及到修改的,可以先刪除,然后重建。

查看存儲過程

1 show create procedure 存儲過程名稱;

可以查看存儲過程詳細創建語句。

 1 mysql> show create procedure sp_test3;
 2 +-----------+------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------+----------------------+--------------------+
 3 | Procedure | sql_mode                                                                                                               | Create Procedure                                                                                                                                                                                                                                  | character_set_client | collation_connection | Database Collation |
 4 +-----------+------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------+----------------------+--------------------+
 5 | sp_test3  | STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION | CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_test3`(sname varchar(20),score DECIMAL(10,2),classid int,out lastid int)
 6 BEGIN
 7 INSERT INTO students(studentname,score,classid) VALUES (sname,score,classid);
 8 select LAST_INSERT_ID() into lastid;
 9 END | utf8                 | utf8_general_ci      | utf8_general_ci    |
10 +-----------+------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------+----------------------+--------------------+
11 1 row in set

小結

存儲過程的優點開篇已經說過了,這邊就不贅述了,個人使用的最大感觸是,盡量不要在應用代碼中寫大量的腳本邏輯,做成存儲過程或者函數會更高效簡潔且易於維護。


免責聲明!

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



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