存儲例程
是存儲程序
的一種類型,本質上也是封裝了一些可執行的語句,只不過它的調用方式是:須要手動去調用!存儲例程
又能夠分爲存儲函數
和存儲過程
,下邊咱們詳細嘮叨這兩個傢伙。mysql
存儲函數
其實就是一種函數
,只不過在這個函數裏能夠執行命令語句而已。函數
的概念你們都應該不陌生,它能夠把處理某個問題的過程封裝起來,以後咱們直接調用函數就能夠去解決一樣的問題了,簡單方便又環保。MySQL
中定義存儲函數
的語句以下:程序員
CREATE FUNCTION 存儲函數名稱([參數列表])
RETURNS 返回值類型
BEGIN
函數體內容
END
複製代碼
從這裏咱們能夠看出,定義一個存儲函數
須要指定函數名稱、參數列表、返回值類型以及函數體內容,若是該函數不須要參數,那參數列表能夠被省略,函數體內容能夠包括一條或多條語句,每條語句都要以分號;
結尾。裏邊的製表符和換行僅僅是爲了好看,若是你以爲煩,徹底能夠用空格代替! 光看定義理解的不深入,咱們先寫一個存儲函數
開開眼:sql
mysql> delimiter $
mysql> CREATE FUNCTION avg_score(s VARCHAR(100))
-> RETURNS DOUBLE
-> BEGIN
-> RETURN (SELECT AVG(score) FROM student_score WHERE subject = s);
-> END $
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
複製代碼
咱們定義了一個名叫avg_score
的函數,它接收一個VARCHAR(100)
類型的參數,聲明的返回值類型是DOUBLE
,須要注意的是,咱們在RETURN
語句後邊寫了一個SELECT
語句,代表這個函數的最後返回結果就是根據這個查詢語句產生的,也就是返回了指定科目的平均成績。編程
咱們自定義的函數和系統內置函數的使用方式是同樣的,都是在函數名後加小括號()
表示函數調用,有參數的函數調用能夠把參數寫到小括號裏邊。函數調用能夠做爲查詢對象或者搜索條件,或者和別的操做數一塊兒組成更復雜的表達式,咱們如今來調用一下剛剛寫好的這個函數吧:bash
mysql> SELECT avg_score('母豬的產後護理');
+------------------------------------+
| avg_score('母豬的產後護理') |
+------------------------------------+
| 73 |
+------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT avg_score('論薩達姆的戰爭準備');
+------------------------------------------+
| avg_score('論薩達姆的戰爭準備') |
+------------------------------------------+
| 73.25 |
+------------------------------------------+
1 row in set (0.00 sec)
mysql>
複製代碼
這樣調用函數就比咱們直接寫兩個又臭又長的查詢語句簡單多了。服務器
若是咱們想查看咱們已經定義了多少個存儲函數,可使用下邊這個語句:編程語言
SHOW FUNCTION STATUS [LIKE 須要匹配的函數名]
複製代碼
因爲這個命令獲得的結果太多,咱們就不演示了哈,本身試試。函數
若是咱們想查看某個函數的具體定義,可使用這個語句:學習
SHOW CREATE FUNCTION 函數名
複製代碼
好比這樣:測試
mysql> SHOW CREATE FUNCTION avg_score\G
*************************** 1. row ***************************
Function: avg_score
sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
Create Function: CREATE DEFINER=`root`@`localhost` FUNCTION `avg_score`(s VARCHAR(100)) RETURNS double
BEGIN
RETURN (SELECT AVG(score) FROM student_score WHERE subject = s);
END
character_set_client: utf8
collation_connection: utf8_general_ci
Database Collation: utf8_general_ci
1 row in set (0.01 sec)
mysql>
複製代碼
忽略結果中咱們看不懂的那些東東,後邊都會詳細嘮叨的,如今主要聚焦在這個函數的定義處。
若是想刪除某個存儲函數,使用這個語句:
DROP FUNCTION 函數名
複製代碼
好比咱們來刪掉avg_score
這個函數:
mysql> DROP FUNCTION avg_score;
Query OK, 0 rows affected (0.00 sec)
mysql>
複製代碼
什麼?你覺得到這裏存儲函數
就嘮叨完了麼?寫完是不可能的,這輩子都不可能寫完的!到如今爲止咱們只是勾勒出一個存儲函數
的大體輪廓,下邊咱們來詳細說一下MySQL
定義函數體時支持的一些語句。
咱們在前邊說過在命令行(黑框框)中自定義變量的方式,它能夠不用聲明就爲變量賦值(也就是調用SET
語句)。而在函數體中使用變量前必須先聲明這個變量,聲明方式以下:
DECLARE 變量名 數據類型 [DEFAULT 默認值];
複製代碼
須要特別留心的是,函數體中的變量名不容許加@
前綴,這一點和黑框框中定義變量的方式是大相徑庭的,特別注意一下。在聲明瞭這個變量以後,纔可使用它:
mysql> delimiter $;
mysql> CREATE FUNCTION var_demo()
-> RETURNS INT
-> BEGIN
-> DECLARE c INT;
-> SET c = 5;
-> RETURN c;
-> END $
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
複製代碼
咱們定義了一個名叫var_demo
並且不須要參數的函數,咱們在函數體中聲明瞭一個名稱爲c
的INT
類型變量,以後咱們調用SET
語句爲這個變量賦值了整數5
,而且把變量c
看成函數結果返回,咱們調用一下這個函數:
mysql> select var_demo();
+------------+
| var_demo() |
+------------+
| 5 |
+------------+
1 row in set (0.00 sec)
mysql>
複製代碼
若是咱們不對聲明的變量賦值的話,它的默認值就是NULL
,固然咱們也能夠經過DEFAULT
子句來顯式的指定變量的默認值,好比這樣:
mysql> delimiter $
mysql> CREATE FUNCTION var_default_demo()
-> RETURNS INT
-> BEGIN
-> DECLARE c INT DEFAULT 1;
-> RETURN c;
-> END $
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql>
複製代碼
在新建立的這個var_default_demo
函數中,咱們聲明瞭一個變量c
,而且指定了它的默認值爲1
,而後看一下函數的調用結果:
mysql> SELECT var_default_demo();
+--------------------+
| var_default_demo() |
+--------------------+
| 1 |
+--------------------+
1 row in set (0.00 sec)
mysql>
複製代碼
獲得的結果是1
,說明了咱們指定的變量默認值生效了!另外,特別須要注意一下咱們能夠將某個查詢語句的結果賦值給變量的狀況,好比咱們改寫一下前邊的avg_score
函數:
CREATE FUNCTION avg_score(s VARCHAR(100))
RETURNS DOUBLE
BEGIN
DECLARE a DOUBLE;
SET a = (SELECT AVG(score) FROM student_score WHERE subject = s);
return a;
END
複製代碼
咱們先把一個查詢語句的結果賦值給了變量a
,而後再返回了這個變量。
在定義函數的時候,能夠指定多個參數,每一個參數都要指定對應的數據類型,就像這樣:
參數名 數據類型
複製代碼
好比咱們上邊編寫的這個avg_score
函數:
CREATE FUNCTION avg_score(s VARCHAR(100))
RETURNS DOUBLE
BEGIN
RETURN (SELECT AVG(score) FROM student_score WHERE subject = s);
END
複製代碼
這個函數只須要一個類型爲VARCHAR(100)
參數,咱們這裏給這個參數起的名稱是s
,須要注意的是,參數名不要和函數體語句中其餘的變量名、命令語句的標識符衝突,好比若是把這個變量名命名爲subject
,它就與下邊用到WHERE
子句中的列名衝突了,致使列名失效。
另外,函數參數不能夠指定默認值,咱們在調用函數的時候,必須顯式的指定全部的參數,而且參數類型也必定要匹配,比方說咱們在調用函數avg_score
時,必須指定咱們要查詢的課程名,否則會報錯的:
mysql> select avg_score();
ERROR 1318 (42000): Incorrect number of arguments for FUNCTION xiaohaizi.avg_score; expected 1, got 0
mysql>
複製代碼
像其餘的編程語言同樣,在MySQL
的函數體裏也可使用判斷的語句,語法格式以下:
IF 布爾表達式 THEN
處理語句
[ELSEIF 布爾表達式 THEN
處理語句]
[ELSE
處理語句]
END IF;
複製代碼
須要注意的是,這裏的處理語句能夠是由多條語句構成的複合語句。咱們舉個例子:
mysql> delimiter $
mysql> CREATE FUNCTION condition_demo(i INT)
-> RETURNS VARCHAR(10)
-> BEGIN
-> DECLARE result VARCHAR(10);
-> IF i = 1 THEN
-> SET result = '結果是1';
-> ELSEIF i = 2 THEN
-> SET result = '結果是2';
-> ELSEIF i = 3 THEN
-> SET result = '結果是3';
-> ELSE
-> SET result = '非法參數';
-> END IF;
-> RETURN result;
-> END $
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql>
複製代碼
在咱們定義的函數condition_demo
中,它接收一個INT
類型的參數,這個函數的處理邏輯以下:
1
,就把result
變量的值設置爲'結果是1'
。2
,就把result
變量的值設置爲'結果是2'
。3
,就把result
變量的值設置爲'結果是3'
。result
變量的值設置爲'非法參數'
。固然了,咱們舉的這個例子仍是比較白癡的啦,固然了,咱們只是爲了說明語法怎麼用,等於到更復雜一點的業務邏輯再往復雜了說哈。咱們如今調用一下這個函數:
mysql> SELECT condition_demo(2);
+-------------------+
| condition_demo(2) |
+-------------------+
| 結果是2 |
+-------------------+
1 row in set (0.00 sec)
mysql> SELECT condition_demo(5);
+-------------------+
| condition_demo(5) |
+-------------------+
| 非法參數 |
+-------------------+
1 row in set (0.00 sec)
mysql>
複製代碼
除了判斷語句,MySQL
還支持循環語句的編寫,不過有3種形式的循環語句,咱們一一道來:
WHILE
循環語句:
WHILE 布爾表達式 DO
循環語句
END WHILE;
複製代碼
這個語句的意思是:若是知足給定的表達式,則執行循環語句,不然退出循環。好比咱們想定義一個從1
到n
這n
個數的和(假設n
大於0
),能夠這麼寫:
mysql> delimiter $
mysql> CREATE FUNCTION sum_all(n INT UNSIGNED)
-> RETURNS INT
-> BEGIN
-> DECLARE result INT DEFAULT 0;
-> DECLARE i INT DEFAULT 1;
-> WHILE i <= n DO
-> SET result = result + i;
-> SET i = i + 1;
-> END WHILE;
-> RETURN result;
-> END $
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql>
複製代碼
在函數sum_all
中,咱們接收一個INT UNSIGNED
類型的參數,聲明瞭兩個INT
類型的變量i
和result
。咱們先測試一下這個函數:
mysql> SELECT sum_all(3);
+------------+
| sum_all(3) |
+------------+
| 6 |
+------------+
1 row in set (0.00 sec)
mysql>
複製代碼
分析一下這個結果是怎麼產生的,初始的狀況下result
的值是0
,i
的值是1
,給定的參數n
的值是3
。這個過程就是:
i <= n
是否成立,顯然成立,進入循環體,將result
的值設置爲1
(result + i
),i
的值爲2
(i + 1
)。i <= n
是否成立,顯然成立,進入循環體,將result
的值設置爲3
(result + i
),i
的值爲3
(i + 1
)。i <= n
是否成立,顯然成立,進入循環體,將result
的值設置爲6
(result + i
),i
的值爲4
(i + 1
)。i <= n
是否成立,顯然不成立,退出循環。因此最後返回的result
的值就是6
,也就是1
、2
、3
這三個數的和。
REPEAT
循環語句
REPEAT
循環和WHILE
循環差很少,只是形式上變了一下:
REPEAT
循環語句
UNTIL 布爾表達式 END REPEAT;
複製代碼
先執行循環語句,再判斷布爾表達式
是否成立,若是成立繼續執行循環語句,不然退出循環。與WHILE
循環不一樣的一點是:WHILE循環先判斷布爾表達式的值,再執行循環語句,REPEAT循環先執行循環語句,再判斷布爾表達式的值,因此至少執行一次循環語句,因此若是sum_all
函數用REPEAT
循環改寫,能夠寫成這樣:
CREATE FUNCTION sum_all(n INT UNSIGNED)
RETURNS INT
BEGIN
DECLARE result INT DEFAULT 0;
DECLARE i INT DEFAULT 1;
REPEAT
SET result = result + i;
SET i = i + 1;
UNTIL i <= n END REPEAT;
RETURN result;
END
複製代碼
LOOP
循環語句
這只是另外一種形式的循環語句:
循環標記:LOOP
循環語句
LEAVE 循環標記;
END LOOP 循環標記;
複製代碼
在LOOP
循環語句中,比較特別的是須要咱們設置循環標記
來標識一個循環,在循環體內依靠 LEAVE 循環標記
的形式來中斷某個循環,比方說咱們能夠把sum_all
函數改寫成這樣:
CREATE FUNCTION sum_all(n INT UNSIGNED)
RETURNS INT
BEGIN
DECLARE result INT DEFAULT 0;
DECLARE i INT DEFAULT 1;
flag:LOOP
IF i > n THEN
LEAVE flag;
END IF;
SET result = result + i;
SET i = i + 1;
END LOOP flag;
RETURN result;
END
複製代碼
其中的flag
就是一個循環標記
,在循環體內判斷i > n
成立的時候就調用LEAVE flag
來跳出這個循環。
不論何時,對語句添加註釋都是一件好事兒!註釋不只僅是幫助別人理解咱們寫的語句是什麼意思,對於咱們本身來講,可能隔了幾天以後再看本身寫的語句就不知道是什麼意思了。在函數體內以--
開頭的語句都算做註釋語句,MySQL
服務器在執行語句的時候會忽略掉這些註釋語句。
-- 函數名:sum_all
-- 參數:n = 從1累加到的數字
CREATE FUNCTION sum_all(n INT UNSIGNED) COMMENT '求1到n這n個數的和'
RETURNS INT
BEGIN
-- 當前累加的和
DECLARE result INT DEFAULT 0;
-- 當前累加的數字
DECLARE i INT DEFAULT 1;
-- 若當前累加的數字不大於指定數字,則繼續執行循環
WHILE i <= n DO
SET result = result + i;
SET i = i + 1;
END WHILE;
-- 返回累加的和
RETURN result;
END
複製代碼
除了--
開頭的語句表示註釋,咱們還能夠在函數參數後寫COMMENT
註釋語句說明這個函數的做用。
本系列專欄都是MySQL入門知識,想看進階知識能夠到小冊中查看:《MySQL是怎樣運行的:從根兒上理解MySQL》的連接 。小冊的內容主要是從小白的角度出發,用比較通俗的語言講解關於MySQL進階的一些核心概念,好比記錄、索引、頁面、表空間、查詢優化、事務和鎖等,總共的字數大約是三四十萬字,配有上百幅原創插圖。主要是想下降普通程序員學習MySQL進階的難度,讓學習曲線更平滑一點~