概述
大多數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
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
小結
存儲過程的優點開篇已經說過了,這邊就不贅述了,個人使用的最大感觸是,盡量不要在應用代碼中寫大量的腳本邏輯,做成存儲過程或者函數會更高效簡潔且易于維護。
為幫助開發者們提升面試技能、有機會入職BATJ等大廠公司,特别制作了這個專輯——這一次整體放出。
大緻内容包括了: Java 集合、JVM、多線程、并發程式設計、設計模式、Spring全家桶、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、MySQL、RabbitMQ、Kafka、Linux、Netty、Tomcat等大廠面試題等、等技術棧!
歡迎大家關注公衆号【Java爛豬皮】,回複【666】,擷取以上最新Java後端架構VIP學習資料以及視訊學習教程,然後一起學習,一文在手,面試我有。
每一個專欄都是大家非常關心,和非常有價值的話題,如果我的文章對你有所幫助,還請幫忙點贊、好評、轉發一下,你的支援會激勵我輸出更高品質的文章,非常感謝!