存儲函數
和存儲過程
都屬於存儲例程
,都是對某些語句的一個封裝。存儲函數
側重於執行這些語句並返回一個值,而存儲過程
更側重於單純的去執行這些語句。先看一下存儲過程
的定義語句:mysql
CREATE PROCEDURE 存儲過程名稱([參數列表])
BEGIN
須要執行的語句
END
複製代碼
與存儲函數
最直觀的不一樣點就是,存儲過程
的定義不須要聲明返回值類型
。爲了更直觀的理解,咱們先定義一個存儲過程
看看:程序員
mysql> delimiter $
mysql> CREATE PROCEDURE t1_operation(
-> m1_value INT,
-> n1_value CHAR(1)
-> )
-> BEGIN
-> SELECT * FROM t1;
-> INSERT INTO t1(m1, n1) VALUES(m1_value, n1_value);
-> SELECT * FROM t1;
-> END $
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql>
複製代碼
咱們創建了一個名叫t1_operation
的存儲過程,它接收兩個參數,一個是INT
類型的,一個是CHAR(1)
類型的。這個存儲過程作了3件事兒,一件是查詢一下t1
表中的數據,第二件是根據接收的參數來向t1
表中插入一條語句,第三件是再次查詢一下t1
表中的數據。sql
存儲函數
執行語句並返回一個值,因此經常使用在表達式中。存儲過程
偏向於調用那些語句,並不能用在表達式中,咱們須要顯式的使用CALL
語句來調用一個存儲過程
:數據庫
CALL 存儲過程([參數列表]);
複製代碼
比方說咱們調用一下t1_operation
存儲過程能夠這麼寫:bash
mysql> CALL t1_operation(4, 'd');
+------+------+
| m1 | n1 |
+------+------+
| 1 | a |
| 2 | b |
| 3 | c |
+------+------+
3 rows in set (0.00 sec)
+------+------+
| m1 | n1 |
+------+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
+------+------+
4 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql>
複製代碼
從執行結果中能夠看到,存儲過程在執行中產生的全部結果集,所有將會被顯示到客戶端。函數
小貼士:
只有查詢語句纔會產生結果集,更新語句是不產生結果集的,因此那條`INSERT`語句所產生的輸出沒有被顯示出來。
複製代碼
與存儲函數
相似,存儲過程
也有類似的查看和刪除語句,咱們下邊只列舉一下相關語句,就不舉例子了。學習
查看當前數據庫中建立的存儲過程
都有哪些的語句:優化
SHOW PROCEDURE STATUS [LIKE 須要匹配的函數名]
複製代碼
查看某個存儲過程
定義的語句:ui
SHOW CREATE PROCEDURE 存儲過程名稱
複製代碼
刪除存儲過程
的語句:spa
DROP PROCEDURE 存儲過程名稱
複製代碼
上邊在嘮叨存儲函數
中使用到的各類語句,包括變量的使用、判斷、循環結構、註釋的使用均可以被用在存儲過程
中,這裏就再也不贅述了。
比存儲函數
牛逼的一點是,存儲過程
在定義參數的時候能夠選擇參數類型(注意!不是數據類型),就像是這個樣子:
參數類型 參數名 數據類型
複製代碼
這個所謂的參數類型
有下邊3種:
參數類型 | 實際參數是否必須是變量 | 描述 |
---|---|---|
IN |
否 | 用於調用者向過程傳遞數據,若是該參數在過程當中被修改,調用者不可見 |
OUT |
是 | 用於把過程產生的結果放到此類型的參數中,過程結束後調用者能夠經過訪問該參數來獲取過程執行的結果 |
INOUT |
是 | 綜合IN 和OUT 的特色,既能夠用於調用者向過程傳遞數據,也能夠用於存放過程當中產生的結果以供調用者使用 |
這麼直接描述有些生硬哈,咱們一個一個來舉例子仔細分析一下:
IN
參數類型
先定義一個類型參數是IN
的存儲過程p_in
:
mysql> delimiter $
複製代碼
mysql> CREATE PROCEDURE p_in ( -> IN arg INT -> ) -> BEGIN -> -- 語句一:讀取參數 -> SELECT arg; -> -- 語句二:爲參數賦值 -> SET arg = 123; -> END $ Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql>
```
這個`p_in`存儲過程只有一個參數`arg`,它的參數類型是`IN`,這個存儲過程實際執行兩個語句,第一個語句是用來讀取參數的值,第二個語句是給參數賦值。咱們調用一下`p_in`:
```
mysql> SET @a = 1;
Query OK, 0 rows affected (0.00 sec)
mysql> CALL p_in(@a);
+------+
| arg |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @a;
+------+
| @a |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
mysql>
```
咱們在客戶端定義了一個變量`a`並賦值`1`,由於它是在客戶端定義的,因此須要加`@`前綴,而後把它看成參數傳給`p_in`存儲過程。從結果中能夠看出,第一個讀取語句被成功執行,雖然第二個語句沒有報錯,可是再存儲過程執行完畢後,再次查看變量`a`的值並無改變,這也就是說:<span style="color:red">IN參數類型的變量只能用於讀取,對類型的變量賦值是不會被調用者看到的</span>。
另外,<span style="color:red">由於對於參數類型是`IN`的參數,咱們只是想在存儲函數執行中使用它,並不須要把執行結果存儲到它裏邊,因此除了讓變量做爲函數參數,常量也是能夠的</span>,好比這樣:
```
mysql> CALL p_in(1);
+------+
| arg |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql>
```
複製代碼
OUT
參數類型
先定義一個類型參數是OUT
的存儲過程p_out
:
mysql> delimiter $
複製代碼
mysql> CREATE PROCEDURE p_out ( -> OUT a INT -> ) -> BEGIN -> SELECT a; -> SET a = 123; -> END $ Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql>
```
這個`p_out`存儲過程只有一個參數`arg`,它的參數類型是`OUT`,`p_out`存儲過程也有兩個語句,一個用於讀取參數的值,另外一個用於爲參數賦值,咱們調用一下`p_out`:
```
mysql> SET @b = 2;
Query OK, 0 rows affected (0.00 sec)
mysql> CALL p_out(@b);
+------+
| a |
+------+
| NULL |
+------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @b;
+------+
| @b |
+------+
| 123 |
+------+
1 row in set (0.00 sec)
mysql>
```
咱們在客戶端定義了一個變量`b`並賦值`2`,而後把它看成參數傳給`p_out`存儲過程。從結果中能夠看出,第一個讀取語句並無獲取到參數的值,在存儲過程執行完畢以後,再次讀取變量`b`的值,發現它的值已經被設置成`123`,說明在過程當中對該變量的賦值對調用者是可見的!這也就是說:<span style="color:red">OUT參數類型的變量只能用於賦值,對類型的變量賦值是會被調用者看到的</span>。
另外,<span style="color:red">因爲`OUT`參數類型的參數只是爲了用於在過程當中賦值後被調用者查看,那實際的參數就不容許是常量,常量還怎麼賦值啊</span>!
複製代碼
INOUT
參數類型
知道了IN
和OUT
參數類型的意思,INOUT
也就明白了,這種類型的參數既能夠在存儲過程當中被讀取,也能夠被賦值後被調用者看到,因此要求實際的參數必須是一個變量,否則還怎麼賦值啊!INOUT
參數類型就不具體舉例子了,你本身試試哈~
須要注意的是,若是咱們不寫明參數類型的話,該參數的類型默認是IN
,咱們以前一直沒有註明參數類型,因此以前使用的參數類型都是IN
!
因爲能夠傳入多個OUT
或者INOUT
類型的參數,因此咱們能夠在一個存儲過程當中得到多個結果,好比這樣:
mysql> delimiter $
mysql> CREATE PROCEDURE get_score_data(
-> OUT max_score DOUBLE,
-> OUT min_score DOUBLE,
-> OUT avg_score DOUBLE,
-> s VARCHAR(100)
-> )
-> BEGIN
-> SELECT MAX(score), MIN(score), AVG(score) FROM student_score WHERE subject = s INTO max_score, min_score, avg_score;
-> END $
Query OK, 0 rows affected (0.02 sec)
mysql> delimiter ;
mysql>
複製代碼
咱們定義的這個get_score_data
存儲過程接受4個參數,前三個參數都是OUT
類型的參數,第四個參數沒寫參數類型,默認就是IN
類型。存儲過程的內容是將指定學科的最高分、最低分、平均分賦值給三個OUT
類型的參數。在這個存儲過程執行完以後,咱們能夠根據經過訪問這幾個OUT
類型的參數來得到相應的最高分、最低分以及平均分:
mysql> CALL get_score_data(@a, @b, @c, '母豬的產後護理');
Query OK, 1 row affected (0.01 sec)
mysql> SELECT @a, @b, @c;
+------+------+------+
| @a | @b | @c |
+------+------+------+
| 100 | 55 | 73 |
+------+------+------+
1 row in set (0.00 sec)
mysql>
複製代碼
這個例子說明了:咱們能夠在存儲過程當中向調用者返回多個值,而存儲函數只能返回一個值。
存儲過程
和存儲函數
都是某些語句的一個封裝,並且使用的語法格式都是同樣的,下邊咱們着重說一下它們的不一樣點以加深你們的對這二者區別的印象:
存儲函數在定義時須要顯式用RETURNS
語句標明返回的數據類型,並且在函數體中必須使用RETURN
語句來顯式指定返回的值,存儲過程不須要。
存儲函數的參數類型只能是IN
,而存儲過程支持IN
、OUT
、INOUT
三種參數類型。
存儲函數只能返回一個值,而存儲過程能夠經過設置多個OUT
類型的參數來返回多個結果。
存儲函數執行過程當中產生的結果集並不會被顯示到客戶端,而存儲過程執行過程當中產生的結果集會被顯示到客戶端。
存儲函數的調用直接使用在表達式中,而存儲過程只能經過CALL
語句來顯式調用。
本系列專欄都是MySQL入門知識,想看進階知識能夠到小冊中查看:《MySQL是怎樣運行的:從根兒上理解MySQL》的連接 。小冊的內容主要是從小白的角度出發,用比較通俗的語言講解關於MySQL進階的一些核心概念,好比記錄、索引、頁面、表空間、查詢優化、事務和鎖等,總共的字數大約是三四十萬字,配有上百幅原創插圖。主要是想下降普通程序員學習MySQL進階的難度,讓學習曲線更平滑一點~