索引
參考文章:MySQL - 索引
視圖
視圖是一個(gè)虛擬表沸久,是從數(shù)據(jù)庫中一個(gè)或多個(gè)表(或已存在的視圖)中抽象而出的一個(gè)邏輯表损俭。視圖并不會(huì)存儲(chǔ)真實(shí)數(shù)據(jù)爱榔,它包含的只是一個(gè) SQL 查詢吝岭,視圖中的數(shù)據(jù)來源于其抽象的基本表,通過視圖可以更加簡(jiǎn)單安全的操作基本表淳玩。
在關(guān)系型數(shù)據(jù)庫中谆奥,每張表的結(jié)構(gòu)都對(duì)應(yīng)真實(shí)業(yè)務(wù)中的一個(gè)實(shí)體信息见坑,因此粹污,當(dāng)一個(gè)大型實(shí)體信息內(nèi)部依賴另一個(gè)實(shí)體信息時(shí)段多,在關(guān)系型數(shù)據(jù)中的表現(xiàn)就是兩個(gè)具備關(guān)聯(lián)關(guān)系的獨(dú)立數(shù)據(jù)表,表的分離存儲(chǔ)優(yōu)點(diǎn)是數(shù)據(jù)更加內(nèi)聚壮吩,存儲(chǔ)空間小进苍,但是增加了復(fù)雜性,比如要查看一個(gè)大型實(shí)體信息完整數(shù)據(jù)鸭叙,則必須進(jìn)行多表連接查詢觉啊,而視圖的出現(xiàn),就可以消除這種復(fù)雜性递雀,只需為這些關(guān)聯(lián)表創(chuàng)建一個(gè)視圖即可柄延,該視圖就可以表達(dá)所需的大型實(shí)體信息。
對(duì)視圖的操作缀程,主要有如下內(nèi)容:
-
創(chuàng)建視圖:視圖是一張?zhí)摫恚渥侄慰梢詠碜砸粡埍淼娜炕虿糠肿侄问锌。部梢詠碜远鄰埍淼母鱾€(gè)字段信息杨凑。MySQL 中創(chuàng)建視圖使用的語句為
CREATE VIEW
,其語法如下所示:CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] [DEFINER = user] [SQL SECURITY { DEFINER | INVOKER }] VIEW view_name [(column_list)] AS select_statement [WITH [CASCADED | LOCAL] CHECK OPTION]
其中:
-
CREATE
:表示創(chuàng)建新視圖摆昧。 -
REPLACE
:表示替換已存在的視圖撩满。 -
ALGORITHM
:表示視圖選擇的算法。其中:-
UNDEFINED
:表示 MySQL 將自動(dòng)選擇算法绅你。 -
MERGE
:表示將使用的視圖語句和視圖定義合并起來伺帘,使得視圖定義的某一部分取代語句對(duì)應(yīng)的部分。 -
TEMPTABLE
:表示將視圖的結(jié)果存入臨時(shí)表中忌锯,然后用臨時(shí)表來執(zhí)行語句伪嫁。
-
-
DEFINER
和SQL SECURITY
:表示視圖調(diào)用使用的訪問權(quán)限檢測(cè)安全上下文。 -
view_name
:表示視圖名稱偶垮。 -
column_list
:表示視圖的屬性列表(多個(gè)字段間使用逗號(hào),
進(jìn)行分隔)张咳。 -
select_statement
:表示SELECT
語句,用于選擇基本表數(shù)據(jù)似舵。 -
[WITH [CASCADED | LOCAL] CHECK OPTION]
:表示視圖在更新時(shí)保證在視圖的權(quán)限范圍之內(nèi)脚猾。其中:-
CASCADED
:該值為默認(rèn)值,表示更新視圖時(shí)要滿足所有相關(guān)視圖和表的條件砚哗。 -
LOCAL
:表示更新視圖時(shí)滿足該視圖本身定義的條件即可龙助。
-
舉個(gè)例子:要求創(chuàng)建如下三個(gè)視圖:
- 為表
comment
創(chuàng)建一個(gè)視圖,用于操作其字段content
- 為表
comment
創(chuàng)建一個(gè)視圖蛛芥,視圖只有一個(gè)字段comment
提鸟,用于操作comment
表中的字段content
脆淹。 - 以表
article
和表comment
作為基本表,創(chuàng)建一個(gè)視圖沽一,要求該視圖包含文章的完整信息:標(biāo)題盖溺,正文,評(píng)論铣缠。
具體代碼如下所示:
# article 初始數(shù)據(jù) mysql> SELECT * FROM article; +----+----------------+---------------+---------+ | id | title | content | pubTime | +----+----------------+---------------+---------+ | 1 | first article | content one | NULL | | 2 | second article | content two | NULL | | 3 | third article | content three | NULL | +----+----------------+---------------+---------+ 3 rows in set (0.00 sec) # comment 初始數(shù)據(jù) mysql> SELECT * FROM comment; +----+------------+------------+ | id | content | article_id | +----+------------+------------+ | 1 | comment 1 | 1 | | 2 | comment 11 | 1 | | 3 | comment 3 | 3 | +----+------------+------------+ 3 rows in set (0.00 sec) # 創(chuàng)建視圖烘嘱,不顯示指定字段,則視圖字段與 SELECT 語句相同 mysql> CREATE VIEW v1_comment AS SELECT content FROM comment; Query OK, 0 rows affected (0.36 sec) # 查看視圖 mysql> SELECT * FROM v1_comment; +------------+ | content | +------------+ | comment 1 | | comment 11 | | comment 3 | +------------+ 3 rows in set (0.00 sec) # 創(chuàng)建視圖蝗蛙,顯示指定字段 mysql> CREATE VIEW v2_comment(comment) AS SELECT content FROM comment; Query OK, 0 rows affected (0.24 sec) # 查看視圖 mysql> SELECT * FROM v2_comment; +------------+ | comment | +------------+ | comment 1 | | comment 11 | | comment 3 | +------------+ 3 rows in set (0.00 sec) # 創(chuàng)建視圖(基于多個(gè)表) mysql> CREATE VIEW v_article_info(title, content, comment) AS SELECT title, a.content, c.content FROM article a LEFT OUTER JOIN comment c ON a.id = c.article_id; Query OK, 0 rows affected (0.36 sec) # 查看視圖 mysql> SELECT * FROM v_article_info; +----------------+---------------+------------+ | title | content | comment | +----------------+---------------+------------+ | first article | content one | comment 11 | | first article | content one | comment 1 | | second article | content two | NULL | | third article | content three | comment 3 | +----------------+---------------+------------+ 4 rows in set (0.00 sec)
-
-
查看視圖:由于視圖也是一張表(虛擬表)蝇庭,因此對(duì)于表的查詢操作(比如:
DESC
,SHOW CREATE TABLE
捡硅,SHOW TABLE STATUS
...)哮内,同樣適用于視圖。同時(shí)壮韭,MySQL 還專門提供了一個(gè)用于查詢視圖詳細(xì)信息的命令:SHOW CREATE VIEW
北发,其語法如下所示:SHOW CREATE VIEW view_name
注:查看視圖必須有
SHOW VIEW
權(quán)限,可以通過 MySQL 內(nèi)置數(shù)據(jù)庫mysql
中表user
進(jìn)行查詢喷屋。舉個(gè)例子:查看視圖
v_article_info
的詳細(xì)信息:mysql> SHOW CREATE VIEW v_article_info\G *************************** 1. row *************************** View: v_article_info Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v_article_info` (`title`,`content`,`comment`) AS select `a`.`title` AS `title`,`a`.`content` AS `content`,`c`.`content` AS `content` from (`article` `a` left join `comment` `c` on((`a`.`id` = `c`.`article_id`))) character_set_client: latin1 collation_connection: latin1_swedish_ci 1 row in set (0.00 sec)
另外琳拨,MySQL 中所有視圖定義都存儲(chǔ)在內(nèi)置數(shù)據(jù)庫
information_schema
的表views
中,因此也可以通過查詢?cè)摫聿榭匆晥D詳細(xì)信息:SELECT * FROM information_schema.views;
-
修改視圖:當(dāng)視圖對(duì)應(yīng)的基本表字段發(fā)生變化的時(shí)候屯曹,可以通過修改視圖來保持視圖字段與基本表一致狱庇,保證后續(xù)操作。
MySQL 提供兩種方式對(duì)視圖進(jìn)行修改:-
使用命令
CREATE OR REPLACE VIEW
來更新視圖恶耽,其語法如下所示:CREATE OR REPLACE [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] [DEFINER = user] [SQL SECURITY { DEFINER | INVOKER }] VIEW view_name [(column_list)] AS select_statement [WITH [CASCADED | LOCAL] CHECK OPTION]
可以看到密任,更新視圖與創(chuàng)建視圖命令完全一致,
CREATE OR REPLACE
命令在視圖存在時(shí)偷俭,會(huì)對(duì)視圖進(jìn)行修改浪讳;當(dāng)視圖不存在時(shí),則創(chuàng)建視圖社搅。舉個(gè)例子:修改視圖
v1_comment
對(duì)應(yīng)的基本表comment
驻债,將comment.content
字段重命名為comment.comment
,此時(shí)查看下視圖v1_comment
結(jié)果形葬;然后更新視圖v1_comment
合呐,再查看下v1_comment
視圖,觀察兩次視圖數(shù)據(jù)差別:# 修改表字段名稱 mysql> ALTER TABLE comment CHANGE content comment tinytext; Query OK, 0 rows affected (0.94 sec) Records: 0 Duplicates: 0 Warnings: 0 # 查看視圖 => 報(bào)錯(cuò)笙以,因此基本表字段被修改 mysql> SELECT * FROM v1_comment; ERROR 1356 (HY000): View 'whyn.v1_comment' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them # 更新視圖 mysql> CREATE OR REPLACE VIEW v1_comment AS SELECT comment FROM comment; Query OK, 0 rows affected (0.41 sec) # 查看視圖 mysql> SELECT * FROM v1_comment; +------------+ | comment | +------------+ | comment 1 | | comment 11 | | comment 3 | +------------+ 3 rows in set (0.01 sec)
由上述例子可以看到淌实,當(dāng)基本表結(jié)構(gòu)變化時(shí),必須同步更新視圖結(jié)構(gòu),才能使用視圖拆祈。
-
使用命令
ALTER VIEW
來修改視圖恨闪,其語法如下所示:ALTER [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] [DEFINER = user] [SQL SECURITY { DEFINER | INVOKER }] VIEW view_name [(column_list)] AS select_statement [WITH [CASCADED | LOCAL] CHECK OPTION]
可以看到,
ALTER VIEW
和CREATE OR REPLACE VIEW
的參數(shù)是一模一樣的放坏,兩者在用法上除了關(guān)鍵字不同咙咽,格式是一致的。ALTER VIEW
區(qū)別于CREATE OR REPLACE VIEW
之處在于前者只能用于修改系統(tǒng)中已存在的視圖淤年。舉個(gè)例子:修改視圖
v_article_info
钧敞,以保證同步更新到表comment
的最新結(jié)構(gòu):# 查看視圖 => 由于基本表 comment 被修改,因此查看失敗 mysql> SELECT * FROM v_article_info; ERROR 1356 (HY000): View 'whyn.v_article_info' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them # 修改視圖麸粮,同步字段信息 mysql> ALTER VIEW v_article_info(title, content, comment) AS -> SELECT title, content, comment FROM article LEFT OUTER JOIN comment ON article.id = comment.article_id; Query OK, 0 rows affected (0.40 sec) # 查看視圖 mysql> SELECT * FROM v_article_info; +----------------+---------------+------------+ | title | content | comment | +----------------+---------------+------------+ | first article | content one | comment 11 | | first article | content one | comment 1 | | second article | content two | NULL | | third article | content three | comment 3 | +----------------+---------------+------------+ 4 rows in set (0.00 sec)
-
-
更新視圖數(shù)據(jù):視圖是一張?zhí)摂M表溉苛,因此可以使用表數(shù)據(jù)操作來實(shí)現(xiàn)對(duì)視圖數(shù)據(jù)的增(
INSERT
)、刪(DELETE
)弄诲、改(UPDATE
)愚战、查(SELECT
)。其中:- 對(duì)視圖的增加齐遵、修改和刪除數(shù)據(jù)操作寂玲,都會(huì)同步轉(zhuǎn)到基本表中進(jìn)行;
- 對(duì)基本表的增加洛搀、修改和刪除數(shù)據(jù)操作敢茁,都會(huì)同步更新到視圖中。
注:并非所有的視圖都是可更新的留美,基本上來說,如果 MySQL 無法確實(shí)定位到被更新的基數(shù)據(jù),則不允許更新操作。通常來說侠讯,定義了以下行為的視圖是不能進(jìn)行更新的:分組(
GROUP BY
)温治、連接、子查詢宽气、組合(UNION
)、聚集函數(shù)、DISTINCT
和計(jì)算列(COUNT()
等)...舉個(gè)例子:對(duì)基本表
comment
進(jìn)行增加(或修改挚币、刪除)操作,查看該操作下視圖v1_comment
的數(shù)據(jù)變化扣典;
然后對(duì)視圖v1_comment
進(jìn)行刪除(或增加妆毕、修改)操作,查看該操作下基本表comment
的數(shù)據(jù)變化:# 基本表 comment 初始數(shù)據(jù) mysql> SELECT * FROM comment; +----+------------+------------+ | id | comment | article_id | +----+------------+------------+ | 1 | comment 1 | 1 | | 2 | comment 11 | 1 | | 3 | comment 3 | 3 | +----+------------+------------+ 3 rows in set (0.01 sec) # 視圖 v1_comment 初始數(shù)據(jù) mysql> SELECT * FROM v1_comment; +------------+ | comment | +------------+ | comment 1 | | comment 11 | | comment 3 | +------------+ 3 rows in set (0.00 sec) # 基本表 comment 添加一條數(shù)據(jù) mysql> INSERT INTO comment(comment, article_id) VALUES ('comment view test', 2); Query OK, 1 row affected (0.38 sec) # 基本表 comment 查詢 mysql> SELECT * FROM comment; +----+-------------------+------------+ | id | comment | article_id | +----+-------------------+------------+ | 1 | comment 1 | 1 | | 2 | comment 11 | 1 | | 3 | comment 3 | 3 | | 6 | comment view test | 2 | +----+-------------------+------------+ 4 rows in set (0.00 sec) # 視圖 v1_comment 查詢 => 可以看到贮尖,基本表增加數(shù)據(jù)笛粘,視圖會(huì)同步進(jìn)行更新 mysql> SELECT * FROM v1_comment; +-------------------+ | comment | +-------------------+ | comment 1 | | comment 11 | | comment 3 | | comment view test | +-------------------+ 4 rows in set (0.00 sec) # 視圖 comment 刪除一條數(shù)據(jù) mysql> DELETE FROM v1_comment WHERE comment = 'comment view test'; Query OK, 1 row affected (0.36 sec) # 查看視圖 => 數(shù)據(jù)已被刪除 mysql> SELECT * FROM v1_comment; +------------+ | comment | +------------+ | comment 1 | | comment 11 | | comment 3 | +------------+ 3 rows in set (0.01 sec) # 查看基本表 => 可以看到,當(dāng)視圖刪除數(shù)據(jù)后,會(huì)同步該操作到基本表中 mysql> SELECT * FROM comment; +----+------------+------------+ | id | comment | article_id | +----+------------+------------+ | 1 | comment 1 | 1 | | 2 | comment 11 | 1 | | 3 | comment 3 | 3 | +----+------------+------------+ 3 rows in set (0.00 sec)
綜上薪前,對(duì)基本表和視圖的修改操作润努,都會(huì)同步到彼此之中。
-
刪除視圖:刪除一個(gè)或多個(gè)視圖采用的命令為
DROP VIEW
示括。其語法如下所示:DROP VIEW [IF EXISTS] view_name [, view_name] ... [RESTRICT | CASCADE]
舉個(gè)例子:刪除視圖
v2_comment
:# 搜索表/視圖 mysql> SHOW tables LIKE 'v2%'; +----------------------+ | Tables_in_whyn (v2%) | +----------------------+ | v2_comment | +----------------------+ 1 row in set (0.00 sec) # 刪除視圖 mysql> DROP VIEW IF EXISTS v2_comment; Query OK, 0 rows affected (0.37 sec) # 刪除視圖成功 mysql> show tables like 'v2%'; Empty set (0.00 sec)
存儲(chǔ)過程
存儲(chǔ)過程是數(shù)據(jù)庫用于封裝一系列 SQL 語句集合的批處理程序單元铺浇,并且支持傳入?yún)?shù)(入?yún)ⅲ瑐鞒鰠?shù)(出參)垛膝,主要用來對(duì)一些具備重復(fù)性復(fù)雜操作進(jìn)行封裝鳍侣,簡(jiǎn)化操作,并且存儲(chǔ)過程創(chuàng)建后保存的是預(yù)編譯結(jié)果繁涂,后續(xù)的調(diào)用過程無需再次編譯拱她,性能更加高效...
MySQL 中,存儲(chǔ)過程程序主要包含『存儲(chǔ)過程(Stored Procedure)』扔罪、『存儲(chǔ)函數(shù)(Stored Function)』秉沼、『觸發(fā)器(Trigger)』、『事件(Event)』和『視圖(View)』矿酵,本章我們只關(guān)注存儲(chǔ)過程和存儲(chǔ)函數(shù)唬复,其他存儲(chǔ)過程程序詳情可查看:Stored Objects
存儲(chǔ)過程可以使用命令CREATE PROCEDURE
進(jìn)行創(chuàng)建,然后使用關(guān)鍵字CALL
進(jìn)行調(diào)用全肮,存儲(chǔ)函數(shù)可以通過命令CREATE FUNCTION
進(jìn)行創(chuàng)建敞咧,其可直接進(jìn)行調(diào)用,就像調(diào)用 MySQL 內(nèi)置函數(shù)辜腺。兩者的區(qū)別是:存儲(chǔ)函數(shù)內(nèi)部必須包含有一個(gè)RETURN
語句休建,并且該RETURN
語句只能返回單個(gè)值或者表對(duì)象,而存儲(chǔ)過程不允許執(zhí)行RETURN
评疗,但是可以通過輸出參數(shù)OUT
返回多個(gè)值...
所有的存儲(chǔ)過程和存儲(chǔ)函數(shù)都存儲(chǔ)在服務(wù)器上测砂,客戶端只需發(fā)送存儲(chǔ)過程名稱和相應(yīng)參數(shù)就可以在服務(wù)器上進(jìn)行調(diào)用,因此在大數(shù)據(jù)量情況下百匆,存儲(chǔ)過程能大幅提升效率砌些。
對(duì)存儲(chǔ)過程程序的操作,主要包含增刪改查以及調(diào)用加匈,具體如下:
-
增:創(chuàng)建存儲(chǔ)過程和創(chuàng)建存儲(chǔ)函數(shù)方法如下:
-
創(chuàng)建存儲(chǔ)過程:創(chuàng)建存儲(chǔ)過程使用的命令為
CREATE PROCEDURE
存璃,其語法如下所示:CREATE [DEFINER = user] PROCEDURE sp_name ([proc_parameter[,...]]) [characteristic ...] routine_body
其中:
DEFINER
:用于指定具備執(zhí)行權(quán)限的用戶。sp_name
:表示存儲(chǔ)過程的名稱雕拼。-
proc_parameter
:表示存儲(chǔ)過程的參數(shù)列表纵东,其格式如下所示:[ IN | OUT | INOUT ] param_name type
其中:
-
IN
:表示輸入?yún)?shù)(可以理解為按值傳遞)。 -
OUT
:表示輸出參數(shù)(可以理解為按引用傳遞悲没,因此可作為返回值)篮迎。 -
INOUT
:表示輸入輸出參數(shù)男图。 -
param_name
:表示參數(shù)名稱。 -
type
:表示參數(shù)類型甜橱,該類型可以是 MySQL 數(shù)據(jù)庫中任意類型逊笆。
-
-
characteristic
:表示存儲(chǔ)過程特性,其值有如下可選:characteristic: { COMMENT 'string' | LANGUAGE SQL | [NOT] DETERMINISTIC | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { DEFINER | INVOKER } }
其中:
-
COMMENT
:表示注釋信息岂傲,可用來描述存儲(chǔ)過程或函數(shù)难裆。 -
LANGUAGE SQL
:表示routine_body
部分是由 SQL 語句組成。 -
[NOT] DETERMINISTIC
:指明存儲(chǔ)過程執(zhí)行結(jié)果是否是確定的镊掖。DETERMINISTIC
表示每次相同的輸入乃戈,執(zhí)行結(jié)果都是相同的輸出(確定);而NOT DETERMINISTIC
表示相同的輸入可能得到不同的輸出(不確定)亩进。默認(rèn)為NOT DETERMINISTIC
症虑。 -
{ CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
:指明子程序使用 SQL 語句的限制,其默認(rèn)值為CONTAINS SQL
归薛。其中:-
CONTAINS SQL
:表示子程序包含 SQL 語句谍憔,但是不包含讀寫數(shù)據(jù)的語句。 -
NO SQL
:表示子程序不包含 SQL 語句主籍。 -
READS SQL DATA
:表示子程序包含讀數(shù)據(jù)的語句习贫。 -
MODIFIES SQL DATA
:表示子程序包含寫數(shù)據(jù)的語句。
-
-
SQL SECURITY { DEFINER | INVOKER }
:用于指定具備執(zhí)行權(quán)限的用戶千元,其默認(rèn)值為DEFINER
苫昌。其中:-
DEFINER
:表示只有定義者才能執(zhí)行。 -
INVOKER
:表示擁有權(quán)限的調(diào)用者可以執(zhí)行幸海。
-
-
-
routine_body
:表示存儲(chǔ)過程主體祟身,即 SQL 語句集合,其格式如下所示:[begin_label:] BEGIN [statement_list] …… END [end_label]
其中:
-
begin_label
/end_label
:表示代碼塊開始/結(jié)束標(biāo)簽物独,end_label
必須與begin_label
相同月而,但也可忽略不寫。 -
BEGIN
:標(biāo)識(shí)代碼塊開始议纯。 -
END
:標(biāo)識(shí)代碼塊結(jié)束。
注:
routine_body
中BEGIN
和END
支持多重嵌套溢谤,其中每條 SQL 語句必須以分號(hào);
進(jìn)行結(jié)尾瞻凤。
注:routine_body
中支持變量定義和賦值,流程控制世杀,條件判斷等高級(jí)程序語言功能阀参,具體內(nèi)容請(qǐng)參考后文。舉個(gè)例子:創(chuàng)建一個(gè)存儲(chǔ)過程
showArticle
瞻坝,用于查看表article
所有內(nèi)容:mysql> DELIMITER // # 臨時(shí)更改分隔符 mysql> CREATE PROCEDURE showArticle() # 創(chuàng)建存儲(chǔ)過程 -> BEGIN -> SELECT * FROM article; -> END // Query OK, 0 rows affected (1.06 sec) mysql> DELIMITER ; # 恢復(fù) SQL 語句分隔符 mysql> CALL showArticle(); # 調(diào)用存儲(chǔ)過程 +----+----------------+---------------+---------+ | id | title | content | pubTime | +----+----------------+---------------+---------+ | 1 | first article | content one | NULL | | 2 | second article | content two | NULL | | 3 | third article | content three | NULL | +----+----------------+---------------+---------+ 3 rows in set (0.10 sec) Query OK, 0 rows affected (0.10 sec)
注:由于 MySQL 命令行程序默認(rèn)的 SQL 語句分隔符為分號(hào)
;
蛛壳,而存儲(chǔ)過程主體代碼塊中每條 SQL 語句必須以分號(hào);
進(jìn)行結(jié)尾,因此默認(rèn)情況下我們無法直接發(fā)送完整的存儲(chǔ)過程給到服務(wù)器端,解決的辦法就是臨時(shí)更改分隔符衙荐,比如DELIMITER //
捞挥,其中,任何字符都可以用作語句分隔符忧吟,除了反斜杠\
砌函,因?yàn)?code>\是 MySQL 內(nèi)置的轉(zhuǎn)義字符。 -
-
創(chuàng)建存儲(chǔ)函數(shù):創(chuàng)建存儲(chǔ)函數(shù)使用的命令為
CREATE FUNCTION
溜族,其語法如下所示:CREATE [DEFINER = user] FUNCTION sp_name ([func_parameter[,...]]) RETURNS type [characteristic ...] routine_body
其中:
RETURNS type
表示存儲(chǔ)函數(shù)返回的類型讹俊,該類型可以是 MySQL 數(shù)據(jù)庫中的任意類型。其他參數(shù)與CREATE PROCEDURE
命令一致煌抒。舉個(gè)例子:定義一個(gè)存儲(chǔ)函數(shù)
showArticleFunc
仍劈,該函數(shù)返回表article
的第一條數(shù)據(jù)記錄的title
:mysql> DELIMITER // mysql> CREATE FUNCTION showArticleFunc() # 創(chuàng)建存儲(chǔ)函數(shù) -> RETURNS VARCHAR(50) -> READS SQL DATA -> NOT DETERMINISTIC -> BEGIN -> RETURN (SELECT title FROM article LIMIT 1); -> END // Query OK, 0 rows affected (0.26 sec) mysql> DELIMITER ; mysql> SELECT showArticleFunc(); # 調(diào)用存儲(chǔ)函數(shù) +-------------------+ | showArticleFunc() | +-------------------+ | first article | +-------------------+ 1 row in set (0.01 sec)
注:默認(rèn)情況下,存儲(chǔ)函數(shù)必須指明至少一個(gè)
DETERMINISTIC
寡壮,NO SQL
或READS SQL DATA
贩疙,否則出錯(cuò)。 調(diào)用:對(duì)于存儲(chǔ)過程诬像,使用關(guān)鍵字
CALL
進(jìn)行調(diào)用屋群。
對(duì)于存儲(chǔ)函數(shù),直接像內(nèi)置函數(shù)一樣進(jìn)行調(diào)用坏挠。-
查:查看存儲(chǔ)過程和存儲(chǔ)函數(shù)有如下三種方法:
- 使用命令
SHOW STATUS
進(jìn)行查看芍躏,其語法如下所示:
SHOW { PROCEDURE | FUNCTION } STATUS [LIKE 'pattern' | WHERE expr]
- 使用命令
SHOW CREATE
進(jìn)行查看,其語法如下所示:
SHOW CREATE { PROCEDURE | FUNCTION } sp_name
- MySQL 中存儲(chǔ)過程和存儲(chǔ)函數(shù)的信息存儲(chǔ)在內(nèi)置數(shù)據(jù)庫
information_schema
的表Routines
中降狠,可以通過查看該表的記錄來查詢存儲(chǔ)過程和存儲(chǔ)函數(shù)的信息对竣,其格式如下所示:
SELECT * FROM information_schema.Routines WHERE ROUTINE_NAME = 'sp_name'
注:要查看系統(tǒng)中存在的所有存儲(chǔ)過程和存儲(chǔ)函數(shù),可以使用如下命令:
SELECT routine_name FROM information_schema.Routines;
- 使用命令
-
改:修改存儲(chǔ)過程和存儲(chǔ)函數(shù)可以使用
ALTER
語句榜配,其語法如下所示:ALTER { PROCEDURE | FUNCTION } sp_name [characteristic ...] characteristic: { COMMENT 'string' | LANGUAGE SQL | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { DEFINER | INVOKER } }
-
刪:刪除存儲(chǔ)過程和存儲(chǔ)函數(shù)可以借助
DROP
命令否纬,其語法如下所示:DROP { PROCEDURE | FUNCTION } [IF EXISTS] sp_name
-
下面介紹下存儲(chǔ)過程和存儲(chǔ)函數(shù)主體塊(即routine_body
中的BEGIN...END
塊)支持的一些高級(jí)語言語法特性:
-
變量:在 MySQL 中,主要存在六種類型變量:『局部變量』,『用戶變量』,『會(huì)話變量』,『全局變量』,『持久化變量』和『參數(shù)』蛋褥。對(duì)變量的操作主要包含『定義』與『賦值』临燃,其中:
-
定義:對(duì)于不同的類型變量,其定義方式不同烙心,具體如下所述:
-
局部變量:局部變量只能在存儲(chǔ)過程
BEGIN...END
塊中使用膜廊,局部變量的定義使用DECLARE
語句,其語法如下所示:DECLARE var_name [, var_name] ... type [DEFAULT value]
-
用戶變量:用戶自定義變量以
@
為前綴淫茵,可以使用SET
命令進(jìn)行設(shè)置爪瓜,其語法如下所示:SET @var_name = expr [, @var_name = expr] ...
注:用戶變量無需聲明與定義,直接使用即可匙瘪。
注:用戶變量在當(dāng)前會(huì)話中有效铆铆,因此可以跨存儲(chǔ)過程進(jìn)行訪問蝶缀。舉個(gè)例子:如下所示:
mysql> SET @v1 = 'user defined variable'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT @v1; +-----------------------+ | @v1 | +-----------------------+ | user defined variable | +-----------------------+ 1 row in set (0.00 sec)
-
會(huì)話變量:會(huì)話變量由系統(tǒng)提供,只在當(dāng)前會(huì)話中有效薄货,其語法如下所示:
@@session.val_name
其中:
-
查看系統(tǒng)所有會(huì)話變量:可使用如下命令:
SHOW SESSION VARIABLES [like_or_where]
-
查看指定會(huì)話:可使用如下命令:
SELECT @@session.val_name
-
查看系統(tǒng)所有會(huì)話變量:可使用如下命令:
-
全局變量:全局變量由系統(tǒng)提供翁都,在整個(gè) MySQL 服務(wù)器內(nèi)都有效,其語法如下所示:
@@global.val_name
其中:
-
查看系統(tǒng)所有全局變量:可使用如下命令:
SHOW GLOBAL VARIABLES [like_or_where]
-
查看指定全局變量值:可使用如下命令:
SELECT @@global.val_name
-
查看系統(tǒng)所有全局變量:可使用如下命令:
-
持久化變量:與全局變量一樣菲驴,持久化變量全局有效荐吵。其語法如下所示:
@@PERSIST.val_name
注:全局變量設(shè)置在數(shù)據(jù)庫重啟后,會(huì)失效赊瞬,而持久化變量其實(shí)就是將全局變量配置到
mysqld-auto.cnf
文件中先煎,下次啟動(dòng)時(shí),加載該配置文件巧涧,讓配置一直有效薯蝎。 參數(shù):參數(shù)是直接在創(chuàng)建存儲(chǔ)過程或存儲(chǔ)函數(shù)時(shí)直接定義,其格式如下所示:
[ IN | OUT | INOUT ] param_name type
其中:
IN
表示入?yún)ⅲ?code>OUT表示出參谤绳,INOUT
表示出參入?yún)⒄季猓唧w內(nèi)容可參考上文。注:參數(shù)具體內(nèi)容是由客戶端調(diào)用時(shí)傳遞給存儲(chǔ)過程程序的缩筛。
-
-
賦值:MySQL 支持兩種變量賦值方法:
-
SELECT...INTO
命令:可以通過SELECT...INTO
命令查詢指定記錄消略,并將結(jié)果賦值給一個(gè)或多個(gè)變量,其語法如下所示:SELECT col_name[,...] INTO var_name[,...] table_expr;
比如:
mysql> SELECT * FROM (VALUES ROW('hello',3)) AS tb INTO @var1, @var2; Query OK, 1 row affected (0.15 sec) mysql> SELECT @var1, @var2; +-------+-------+ | @var1 | @var2 | +-------+-------+ | hello | 3 | +-------+-------+ 1 row in set (0.00 sec)
注:上述示例中瞎抛,
VALUES
關(guān)鍵字用于創(chuàng)建一個(gè)表艺演,ROW()
函數(shù)用于填充一行的內(nèi)容,所以VALUES ROW('hello', 3)
實(shí)際上是創(chuàng)建了一個(gè)表桐臊,如下所示:mysql> VALUES ROW('hello', 3); +----------+----------+ | column_0 | column_1 | +----------+----------+ | hello | 3 | +----------+----------+ 1 row in set (0.00 sec)
-
SET
命令:為變量賦值胎撤,可以使用SET
命令,其語法如下所示:SET variable = expr [, variable = expr] ... variable: { user_var_name | param_name | local_var_name | {GLOBAL | @@GLOBAL.} system_var_name | {PERSIST | @@PERSIST.} system_var_name | {PERSIST_ONLY | @@PERSIST_ONLY.} system_var_name | [SESSION | @@SESSION. | @@] system_var_name }
其中:
-
局部變量 / 參數(shù):其賦值方法如下:
mysql> DELIMITER // mysql> CREATE PROCEDURE test1( IN var1 VARCHAR(50) ) # 參數(shù) -> BEGIN -> DECLARE var2 VARCHAR(50) DEFAULT 'undefined'; # 定義局部變量 -> SET var2 = var1; # 局部變量賦值 -> SET var1 = 'change argument value'; # 參數(shù)賦值 -> SELECT var1, var2; -> END // Query OK, 0 rows affected (0.34 sec) mysql> CALL test1(@myvar) // # 調(diào)用存儲(chǔ)過程断凶,此時(shí) @myvar = NULL +-----------------------+------+ | var1 | var2 | +-----------------------+------+ | change argument value | NULL | +-----------------------+------+ 1 row in set (0.00 sec) Query OK, 0 rows affected (0.00 sec) mysql> SELECT @myvar // # IN 參數(shù)是按值傳遞伤提,故函數(shù)內(nèi)更改,不影響外部變量 +--------+ | @myvar | +--------+ | NULL | +--------+ 1 row in set (0.00 sec) mysql> SET @myvar = 'user variable' // Query OK, 0 rows affected (0.00 sec) mysql> CALL test1(@myvar) // +-----------------------+---------------+ | var1 | var2 | +-----------------------+---------------+ | change argument value | user variable | +-----------------------+---------------+ 1 row in set (0.00 sec) Query OK, 0 rows affected (0.00 sec) mysql> DELIMITER ;
-
用戶變量:其賦值方法如下:
SET @myvar = expr;
-
會(huì)話變量:其賦值方法如下所示:
# 法一:使用 SESSION 關(guān)鍵字 SET SESSION sql_mode = 'TRADITIONAL'; # 法二:使用 LOCAL 關(guān)鍵字 SET LOCAL sql_mode = 'TRADITIONAL'; # 法三:使用前綴 @@ SET @@SESSION.sql_mode = 'TRADITIONAL'; SET @@LOCAL.sql_mode = 'TRADITIONAL'; # 可忽略 SESSION 或 LOCAL 關(guān)鍵字 SET @@sql_mode = 'TRADITIONAL'; # 可忽略 @@ 前綴 SET sql_mode = 'TRADITIONAL';
-
全局變量:其賦值方法如下所示:
# 法一:使用 GLOBAL 關(guān)鍵字 SET GLOBAL max_connections = 1000; # 法二:使用前綴 @@ SET @@GLOBAL.max_connections = 1000;
-
持久化變量:其賦值方法如下所示:
# 法一:使用 PERSIST 關(guān)鍵字 SET PERSIST max_connections = 1000; # 法二:使用前綴 @@ SET @@PERSIST.max_connections = 1000;
-
-
-
-
流程控制:流程控制語句可以根據(jù)條件判斷來控制語句的執(zhí)行流程认烁。MySQL 中流程控制語句可以分為如下幾類:
-
條件判斷:包含兩種條件判斷語句:
-
IF
:其語法如下所示:IF search_condition THEN statement_list [ELSEIF search_condition THEN statement_list] ... [ELSE statement_list] END IF
其中:當(dāng)
search_condition
為真(TRUE
)時(shí)肿男,執(zhí)行THEN
語句,否則執(zhí)行ELSEIF
或ELSE
語句却嗡。舉個(gè)例子:如下所示:
mysql> DELIMITER // mysql> CREATE PROCEDURE test(IN var VARCHAR(20)) -> BEGIN -> IF var IS NULL THEN -> SELECT 'argument is null'; -> ELSE -> SELECT CONCAT('argument exists: ',var); -> END IF; -> END // Query OK, 0 rows affected (0.31 sec) mysql> DELIMITER ; mysql> CALL test(@var); +------------------+ | argument is null | +------------------+ | argument is null | +------------------+ 1 row in set (0.09 sec) Query OK, 0 rows affected (0.09 sec) mysql> SET @var = 'hello'; Query OK, 0 rows affected (0.04 sec) mysql> CALL test(@var); +---------------------------------+ | concat('argument exists: ',var) | +---------------------------------+ | argument exists: hello | +---------------------------------+ 1 row in set (0.00 sec) Query OK, 0 rows affected (0.00 sec)
-
CASE
:其語法如下所示:# 格式一 CASE case_value WHEN when_value THEN statement_list [WHEN when_value THEN statement_list] ... [ELSE statement_list] END CASE # 格式二 CASE WHEN search_condition THEN statement_list [WHEN search_condition THEN statement_list] ... [ELSE statement_list] END CASE
舉個(gè)例子:如下所示:
mysql> DELIMITER // mysql> CREATE PROCEDURE test(IN var INT) -> BEGIN -> CASE @var -> WHEN 1 THEN SELECT 1; -> WHEN 2 THEN SELECT 2; -> ELSE SELECT 'unknown'; -> END CASE; -> END // Query OK, 0 rows affected (0.38 sec) mysql> DELIMITER ; mysql> CALL test(@var); +---------+ | unknown | +---------+ | unknown | +---------+ 1 row in set (0.13 sec) Query OK, 0 rows affected (0.13 sec) mysql> SET @var = 2; Query OK, 0 rows affected (0.00 sec) mysql> CALL test(@var); +---+ | 2 | +---+ | 2 | +---+ 1 row in set (0.03 sec) Query OK, 0 rows affected (0.03 sec)
-
-
循環(huán):MySQL 提供三種循環(huán)控制語句次伶,如下所示:
-
WHILE
循環(huán):其語法如下所示:[begin_label:] WHILE search_condition DO statement_list END WHILE [end_label]
舉個(gè)例子:循環(huán)打印數(shù)字 1 ~ 10:
mysql> DELIMITER // mysql> CREATE PROCEDURE test(OUT result CHAR(20)) -> BEGIN -> DECLARE i INT DEFAULT 1; -> DECLARE str CHAR(20) DEFAULT '1'; -> WHILE i < 10 DO -> SET i = i+1; -> SET str = CONCAT(str, ',', i); -> END WHILE; -> SET result = str; -> END // Query OK, 0 rows affected (0.23 sec) mysql> DELIMITER ; mysql> CALL test(@var1); Query OK, 0 rows affected (0.02 sec) mysql> SELECT @var1; +----------------------+ | @var1 | +----------------------+ | 1,2,3,4,5,6,7,8,9,10 | +----------------------+ 1 row in set (0.00 sec)
注:由于字符串拼接函數(shù)
CONCAT()
參數(shù)只要有一個(gè)為NULL
,則其返回值為NULL
稽穆,因此這里創(chuàng)建一個(gè)帶有默認(rèn)值的局部變量str
,用于進(jìn)行拼接赶撰,避免外部變量直接拼接(因?yàn)橥獠孔兞靠赡転?code>NULL)舌镶,最后再賦值給外部變量即可確保拼接成功柱彻。 -
LOOP
死循環(huán):其語法如下所示:[begin_label:] LOOP statement_list END LOOP [end_label]
-
REPEAT
重復(fù):其語法如下所示:[begin_label:] REPEAT statement_list UNTIL search_condition END REPEAT [end_label]
舉個(gè)例子:循環(huán)打印數(shù)字 1 ~ 10:
mysql> DELIMITER // mysql> CREATE FUNCTION test() -> RETURNS VARCHAR(30) -> DETERMINISTIC -> BEGIN -> DECLARE i INT DEFAULT 1; -> DECLARE str VARCHAR(30) DEFAULT '1'; -> REPEAT -> SET i = i + 1; -> SET str = CONCAT(str, '-', i); -> UNTIL i>=10 END REPEAT; -> RETURN str; -> END // Query OK, 0 rows affected (1.15 sec) mysql> DELIMITER ; mysql> SELECT test(); +----------------------+ | test() | +----------------------+ | 1-2-3-4-5-6-7-8-9-10 | +----------------------+ 1 row in set (0.04 sec)
-
-
中斷控制語句:中斷跳轉(zhuǎn)控制語句主要有三個(gè):
-
LEAVE
:該語句用于跳出循環(huán),與高級(jí)語言(比如 Java)的break
功能一致餐胀。其語法如下所示:LEAVE label
-
INTERATE
:該語句用于繼續(xù)執(zhí)行循環(huán)哟楷,與高級(jí)語言(比如 Java)的continue
功能一致。其語法如下所示:ITERATE label
-
RETURN
:該語句用于結(jié)束存儲(chǔ)過程函數(shù)否灾,并返回一個(gè)值卖擅。其語法如下所示:RETURN expr
注:
RETURN
只用于存儲(chǔ)函數(shù),不作用于其他存儲(chǔ)程序(如存儲(chǔ)過程墨技,觸發(fā)器惩阶,事件等)。
注:存儲(chǔ)函數(shù)至少包含一個(gè)RETURN
語句扣汪。
舉個(gè)例子:存儲(chǔ)一個(gè)存儲(chǔ)函數(shù)断楷,其內(nèi)有一個(gè)死循環(huán)
loop
,要求打印出 0 ~ 9 個(gè)數(shù):mysql> DELIMITER // mysql> CREATE FUNCTION test() -> RETURNS VARCHAR(30) -> DETERMINISTIC -> BEGIN -> DECLARE i INT DEFAULT 0; -> DECLARE str VARCHAR(30) DEFAULT ''; -> label_loop: LOOP -> IF i < 10 THEN -> SET str = CONCAT(str,' ',i); -> SET i = i + 1; -> ITERATE label_loop; # 繼續(xù)循環(huán) -> END IF; -> LEAVE label_loop; # 跳出 loop 死循環(huán) -> END LOOP label_loop; -> RETURN str; -> END // Query OK, 0 rows affected (0.36 sec) mysql> DELIMITER ; mysql> SELECT test(); +----------------------+ | test() | +----------------------+ | 0 1 2 3 4 5 6 7 8 9 | +----------------------+ 1 row in set (0.00 sec)
-
-
-
條件處理:在存儲(chǔ)過程程序運(yùn)行過程中崭别,可能會(huì)遇到一些突發(fā)場(chǎng)景需要進(jìn)行處理冬筒,比如產(chǎn)生異常、發(fā)生錯(cuò)誤等茅主,此時(shí)可以使用一些特殊的
Condition
來捕獲這些異常舞痰,并進(jìn)行處理(由該Condition
指向的處理器Handler
進(jìn)行處理)。這里就涉及到Condition
的定義與處理器Handler
的使用诀姚,具體如下:-
條件定義:定義
Condition
的語法如下所示:DECLARE condition_name CONDITION FOR condition_value condition_value: { mysql_error_code | SQLSTATE [VALUE] sqlstate_value }
注:條件定義必須位于游標(biāo)和處理器
Handler
聲明之前响牛。
其中:-
condition_name
:表示條件的名稱。 -
condition_value
:表示錯(cuò)誤碼学搜。其有兩種取值范圍:-
mysql_error_code
:一個(gè)數(shù)值類型的錯(cuò)誤碼娃善。
注:不要使用0
作為錯(cuò)誤碼,因?yàn)?MySQL 中0
表示成功瑞佩,而不是錯(cuò)誤標(biāo)識(shí)聚磺。 \ -
sqlstate_value
:一個(gè)長(zhǎng)度為5
的字符串類型錯(cuò)誤代碼。
注:不要使用以'00'
開頭的SQLSTATE
錯(cuò)誤碼炬丸,因此 MySQL 將其視為成功而不是錯(cuò)誤瘫寝。
-
注:MySQL 內(nèi)置的完整錯(cuò)誤碼表(包含
mysql_error_code
及其對(duì)應(yīng)的sqlstate_value
)可查看:Server Error Message Reference舉個(gè)例子:要求定義
"ERROR 1148(42000)"
錯(cuò)誤,名稱為command_not_allowed
稠炬,可使用如下兩種方式定義:# 方法一:使用 sqlstate_value DECLARE command_not_allowed CONDITION FOR '42000'; # 方法二:使用 mysql_error_code DECLARE command_not_allowed CONDITION FOR 1148;
-
-
處理器
Handler
:定義處理器的語法如下所示:DECLARE handler_action HANDLER FOR condition_value [, condition_value] ... statement handler_action: { CONTINUE | EXIT | UNDO } condition_value: { mysql_error_code | SQLSTATE [VALUE] sqlstate_value | condition_name | SQLWARNING | NOT FOUND | SQLEXCEPTION }
處理器可用于處理一個(gè)或多個(gè)條件
Condition
焕阿,當(dāng)與其綁定的條件產(chǎn)生時(shí),statement
塊就會(huì)被調(diào)用首启。其中:-
handler_action
:表示處理器在執(zhí)行完statement
語句塊后采取的操作暮屡,其值有:-
CONTINUE
:表示忽略錯(cuò)誤,繼續(xù)執(zhí)行毅桃。 -
EXIT
:表示遇到錯(cuò)誤后褒纲,退出定義當(dāng)前處理器的BEGIN...END
塊准夷。 -
UNDO
:表示遇到錯(cuò)誤后撤回之前的操作。MySQL 中暫不支持該操作莺掠。
-
-
condition_value
:表示錯(cuò)誤類型衫嵌。其取值范圍有如下可選:-
mysql_error_code
:匹配數(shù)值類型錯(cuò)誤碼。 -
sqlstate_value
:匹配長(zhǎng)度為5
的字符串類型錯(cuò)誤碼彻秆。 -
condition_name
:表示匹配以DECLARE...Condition
定義的錯(cuò)誤條件名楔绞。 -
SQLWARNING
:匹配以所有01
開頭的SQLSTATE
錯(cuò)誤碼。 -
NOT FOUND
:表示匹配所有以02
開頭的SQLSTATE
錯(cuò)誤碼唇兑。 -
SQLEXCEPTION
:表示匹配所有除SQLWARNING
和NOT FOUND
之外的SQLSTATE
錯(cuò)誤碼酒朵。
-
-
statement
:表示當(dāng)一個(gè)或多個(gè)綁定條件產(chǎn)生時(shí),觸發(fā)執(zhí)行的處理器代碼塊幔亥。該代碼塊可以是簡(jiǎn)單的一條語句耻讽,也可以是BEGIN...END
構(gòu)成的多語句塊。
-
-
綜上:DECLARE...CONDITION
語句其實(shí)就是命名(condition_name
)了一個(gè)錯(cuò)誤條件(錯(cuò)誤條件其實(shí)就是一個(gè)錯(cuò)誤碼(condition_value
))帕棉,通常我們還需要定義一個(gè)處理器Handler
綁定這個(gè)錯(cuò)誤條件针肥,這樣當(dāng)系統(tǒng)產(chǎn)生這個(gè)錯(cuò)誤條件時(shí),處理器就能進(jìn)行捕獲并處理香伴。
舉個(gè)例子:創(chuàng)建一個(gè)存儲(chǔ)過程慰枕,并為其添加'23000'
錯(cuò)誤捕獲,輸出信息提示:
mysql> CREATE TABLE tmp (
-> id INT,
-> PRIMARY KEY(id)
-> );
Query OK, 0 rows affected (1.29 sec)
mysql> DELIMITER //
mysql> CREATE PROCEDURE handle23000()
-> BEGIN
-> # 創(chuàng)建錯(cuò)誤捕獲處理
-> DECLARE EXIT HANDLER FOR SQLSTATE '23000' SELECT 'detected ERROR(23000)';
-> SET @x = 1;
-> INSERT INTO tmp VALUES(1);
-> SET @x = 2;
-> INSERT INTO tmp VALUES(1); # error occured
-> SET @x = 3;
-> END //
Query OK, 0 rows affected (0.35 sec)
mysql> DELIMITER ;
mysql> CALL handle23000();
+-----------------------+
| detected ERROR(23000) |
+-----------------------+
| detected ERROR(23000) |
+-----------------------+
1 row in set (0.15 sec)
Query OK, 0 rows affected (0.15 sec)
mysql> SELECT @x;
+------+
| @x |
+------+
| 2 |
+------+
1 row in set (0.00 sec)
存儲(chǔ)過程handle23000
在第二次進(jìn)行插入操作時(shí)即纲,由于表tmp
已存在相同主鍵具帮,此時(shí) MySQL 會(huì)拋出錯(cuò)誤'23000'
低斋,然后處理器就會(huì)捕獲到該錯(cuò)誤,從而執(zhí)行SELECT 'detected ERROR(23000)'
語句掘猿,又由于處理器我們?cè)O(shè)置了EXIT
唇跨,即遇到錯(cuò)誤代碼后稠通,就直接退出當(dāng)前代碼塊(BEGIN...END
),因此會(huì)話變量@x
的值為2
买猖,而如果將EXIT
改為CONTINUE
改橘,則遇到錯(cuò)誤后,會(huì)繼續(xù)執(zhí)行后續(xù)代碼玉控,因此此時(shí)的@x = 3
飞主。
注:可以為錯(cuò)誤碼23000
定義名稱,更加語義化:
CREATE PROCEDURE handle23000()
BEGIN
DECLARE whyn_primary_key_already_exists CONDITION FOR SQLSTATE '23000';
DECLARE EXIT HANDLER FOR whyn_primary_key_already_exists SELECT 'detected ERROR(23000)';
...
END //
-
游標(biāo)(cursor):如果查詢語句返回多條記錄,則可以使用游標(biāo)來逐條讀取結(jié)果集記錄既棺。
對(duì)游標(biāo)的操作依次包含如下:-
聲明:聲明游標(biāo)使用
DECLARE
關(guān)鍵字讽挟,其語法如下所示:DECLARE cursor_name CURSOR FOR select_statement
其中:
-
cursor_name
:表示游標(biāo)的名稱。 -
select_statement
:表示查詢語句SELECT
丸冕,其返回的就是游標(biāo)需要進(jìn)行遍歷的結(jié)果集。
注:游標(biāo)的聲明必須在處理器
Handler
之前薛窥,在變量和條件聲明之后胖烛。 -
-
打開:打開游標(biāo)使用
OPEN
關(guān)鍵字,其語法如下:OPEN cursor_name
-
使用:打開游標(biāo)后诅迷,就可以使用該游標(biāo)遍歷結(jié)果集佩番。其語法如下所示:
FETCH [[NEXT] FROM] cursor_name INTO var_name [, var_name] ...
其中:
-
cursor_name
:表示游標(biāo)的名稱罢杉。 -
var_name
:表示游標(biāo)查詢出來的一條記錄賦值給到的變量赋秀。
注:FETCH
的參數(shù)個(gè)數(shù)(即var_name
)必須匹配游標(biāo)SELECT
返回結(jié)果集的列數(shù)。
使用游標(biāo)遍歷結(jié)果集時(shí)著洼,游標(biāo)會(huì)自動(dòng)將當(dāng)前遍歷到的一行記錄賦值給相應(yīng)參數(shù),然后自動(dòng)遍歷下一行記錄液荸,依次重復(fù)上述步驟,直至遍歷到結(jié)果集末尾忍弛,此時(shí)會(huì)拋出一個(gè)沒有數(shù)據(jù)的
Condition
(即'02000'
)细疚,我們可以設(shè)置一個(gè)Handler
來檢測(cè)該條件(或者檢測(cè)NOT FOUND
條件)然遏。 -
-
關(guān)閉:關(guān)閉游標(biāo)使用
CLOSE
關(guān)鍵字,其語法如下:CLOSE cursor_name
注:如果沒有顯示關(guān)閉游標(biāo)秧倾,則在超過游標(biāo)定義的
BEGIN...END
塊后那先,會(huì)自動(dòng)進(jìn)行關(guān)閉。
綜上揖闸,游標(biāo)的一套組合拳就是:聲明 -> 打開 -> 使用(遍歷)-> 關(guān)閉。
舉個(gè)例子:遍歷表
article
蹲嚣,要求依次輸出該表的所有內(nèi)容:標(biāo)題 + 正文 + 評(píng)論:mysql> DELIMITER // mysql> CREATE PROCEDURE traverseArticle() -> BEGIN -> # 遍歷完成標(biāo)識(shí) -> DECLARE done BOOLEAN DEFAULT FALSE; -> DECLARE _title VARCHAR(50) DEFAULT ''; -> DECLARE _content TEXT DEFAULT ''; -> DECLARE _comment TINYTEXT; -> -> # 定義游標(biāo) -> DECLARE mycursor CURSOR FOR SELECT title, content, comment FROM article LEFT OUTER JOIN comment ON article.id = comment.article_id; -> # 游標(biāo)遍歷到尾部產(chǎn)生的錯(cuò)誤條件 -> DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; -> -> OPEN mycursor; -> -> label_loop_article: LOOP -> FETCH mycursor INTO _title, _content, _comment; -> # 直接輸出 -> SELECT _title, _content, _comment; -> -> IF done THEN -> LEAVE label_loop_article; -> END IF; -> END LOOP; -> CLOSE mycursor; -> END // Query OK, 0 rows affected (0.26 sec) mysql> DELIMITER ; mysql> CALL traverseArticle(); +---------------+-------------+------------+ | _title | _content | _comment | +---------------+-------------+------------+ | first article | content one | comment 11 | +---------------+-------------+------------+ 1 row in set (0.00 sec) +---------------+-------------+-----------+ | _title | _content | _comment | +---------------+-------------+-----------+ | first article | content one | comment 1 | +---------------+-------------+-----------+ 1 row in set (0.00 sec) ...
注:在 MySQL 中说贝,布爾類型
BOOLEAN
其實(shí)就是TINYINT(1)
類型言询,其中:0
為false
,非0
為true
辆憔。 -
更多存儲(chǔ)過程內(nèi)容熊榛,可以參考:
觸發(fā)器
觸發(fā)器(trigger)是一個(gè)特殊的存儲(chǔ)過程程序,它預(yù)先定義了六個(gè)事件,分別為BEFORE INSERT
、AFTER INSERT
帖世、BEFORE UPDATE
、AFTER UPDATE
哪轿、BEFORE DELETE
和AFTER DELETE
,當(dāng) MYSQL 對(duì)表執(zhí)行INSERT
飘痛、UPDATE
或DELETE
操作時(shí),會(huì)自動(dòng)觸發(fā)相應(yīng)觸發(fā)器執(zhí)行相應(yīng)操作塑猖。
注:存儲(chǔ)過程需手動(dòng)使用CALL
進(jìn)行調(diào)用,而觸發(fā)器是監(jiān)聽某些事件自動(dòng)被觸發(fā)執(zhí)行的。
對(duì)觸發(fā)器的操作包含如下內(nèi)容:
-
創(chuàng)建:創(chuàng)建觸發(fā)器的語法如下所示:
CREATE [DEFINER = user] TRIGGER trigger_name trigger_time trigger_event ON tbl_name FOR EACH ROW [trigger_order] trigger_body trigger_time: { BEFORE | AFTER } trigger_event: { INSERT | UPDATE | DELETE } trigger_order: { FOLLOWS | PRECEDES } other_trigger_name
注:只有表才支持觸發(fā)器巍虫,臨時(shí)表和視圖均不支持俯抖。
其中:
-
DEFINER
:指觸發(fā)器觸發(fā)時(shí)進(jìn)行訪問檢測(cè)所使用的賬戶。 -
trigger_name
:表示觸發(fā)器的名稱柬祠。 -
trigger_time
:表示觸發(fā)時(shí)機(jī),其值可為BEFORE
或AFTER
二選一。 -
trigger_event
:表示觸發(fā)事件毯盈,包括INSERT
、UPDATE
和DELETE
可選。 -
tbl_name
:表示觸發(fā)器關(guān)聯(lián)的表名捺信,即在哪張表上建立觸發(fā)器。 -
trigger_order
:表示觸發(fā)順序掌挚,可以指定當(dāng)前觸發(fā)器在指定觸發(fā)器觸發(fā)前/后再觸發(fā)陡厘。 -
trigger_body
:表示觸發(fā)器執(zhí)行語句。該語句可以是一個(gè)簡(jiǎn)單的語句,也可以是一個(gè)語句塊(BEGIN...END
)。
注:
trigger_time
和trigger_event
兩兩結(jié)合嗤疯,總共可生成以下六種事件:BEFORE INSERT
/AFTER INESRT
:在INSERT
觸發(fā)器代碼內(nèi)辟汰,可引用一個(gè)名為NEW
的虛擬表凑术,該表內(nèi)容為要進(jìn)行插入的數(shù)據(jù)扶踊。
注:對(duì)于AUTO_INCREMENT
列,NEW
在before insert
中為0
,在after insert
中包含新的自動(dòng)生成值珠闰。
注:在BEFORE INSERT
事件中,可通過更改NEW
表中的相關(guān)字段來更改插入的值裸影。**
BEFORE DELETE
/ **AFTER DELETE
:在DELETE
觸發(fā)器代碼內(nèi)界轩,可以應(yīng)用一個(gè)名為OLD
的虛擬表,該表內(nèi)容為要進(jìn)行刪除的數(shù)據(jù)。
注:OLD
表中數(shù)據(jù)均為只讀數(shù)據(jù)偷办,不能進(jìn)行修改。**
BEFORE UPDATE
/ **AFTER UPDATE
:在UPDATE
觸發(fā)器代碼內(nèi)澄港,可以引用一個(gè)名為NEW
和OLD
的虛擬表椒涯,NEW
表中包含更新的數(shù)據(jù),而OLD
表中包含舊的數(shù)據(jù)(暫未被更新的數(shù)據(jù))回梧。
注:在BEFORE UPDATE
事件中废岂,可通過更改NEW
表中的相關(guān)字段來更改插入的值。
舉個(gè)例子:創(chuàng)建一個(gè)表
tmp
,為其添加所有的觸發(fā)器摘仅,分別為表執(zhí)行INSERT
掏击、UPDATE
和DELETE
操作滚粟,查看相應(yīng)觸發(fā)器情況:# 建表 mysql> CREATE TABLE tmp ( -> id INT PRIMARY KEY AUTO_INCREMENT, -> data VARCHAR(50) NOT NULL -> ); Query OK, 0 rows affected (1.23 sec) # INSERT Trigger mysql> CREATE TRIGGER tri_before_insert BEFORE INSERT ON tmp FOR EACH ROW SET @before_insert = CONCAT(NEW.id, ' ', NEW.data); Query OK, 0 rows affected (1.48 sec) mysql> CREATE TRIGGER tri_after_insert AFTER INSERT ON tmp FOR EACH ROW SET @after_insert = CONCAT(NEW.id, ' ', NEW.data); Query OK, 0 rows affected (0.30 sec) # DELETE Trigger mysql> CREATE TRIGGER tri_before_delete BEFORE DELETE ON tmp FOR EACH ROW SET @before_delete = CONCAT(OLD.id, ' ', OLD.data); Query OK, 0 rows affected (0.29 sec) mysql> CREATE TRIGGER tri_after_delete AFTER DELETE ON tmp FOR EACH ROW SET @after_delete = CONCAT(OLD.id, ' ', OLD.data); Query OK, 0 rows affected (0.32 sec) mysql> DELIMITER // # UPDATE Trigger mysql> CREATE TRIGGER tri_before_update BEFORE UPDATE ON tmp FOR EACH ROW -> BEGIN -> SET @before_update_old = CONCAT(OLD.id, ' ', OLD.data); -> SET @before_update_new = CONCAT(NEW.id, ' ', NEW.data); -> END // Query OK, 0 rows affected (0.68 sec) mysql> CREATE TRIGGER tri_after_update AFTER UPDATE ON tmp FOR EACH ROW -> BEGIN -> set @after_update_old = CONCAT(OLD.id, ' ', OLD.data); -> SET @after_update_new = CONCAT(NEW.id, ' ', NEW.data); -> END // Query OK, 0 rows affected (0.27 sec) mysql> DELIMITER ; # 執(zhí)行插入操作 mysql> INSERT INTO tmp(data) VALUES ('first data'); Query OK, 1 row affected (0.17 sec) # 查看 INSERT 觸發(fā)器 mysql> SELECT @before_insert, @after_insert; +----------------+---------------+ | @before_insert | @after_insert | +----------------+---------------+ | 0 first data | 1 first data | +----------------+---------------+ 1 row in set (0.00 sec) # 執(zhí)行更新操作 mysql> UPDATE tmp SET data = 'update first data' WHERE data = 'first data'; Query OK, 1 row affected (0.28 sec) Rows matched: 1 Changed: 1 Warnings: 0 # 查看 UPDATE 觸發(fā)器 mysql> SELECT @before_update_old, @before_update_new, @after_update_old, @after_update_new; +--------------------+---------------------+-------------------+---------------------+ | @before_update_old | @before_update_new | @after_update_old | @after_update_new | +--------------------+---------------------+-------------------+---------------------+ | 1 first data | 1 update first data | 1 first data | 1 update first data | +--------------------+---------------------+-------------------+---------------------+ 1 row in set (0.00 sec) # 執(zhí)行刪除操作 mysql> DELETE FROM tmp WHERE data = 'update first data'; Query OK, 1 row affected (0.42 sec) # 查看 DELETE 觸發(fā)器 mysql> SELECT @before_delete, @after_delete; +---------------------+---------------------+ | @before_delete | @after_delete | +---------------------+---------------------+ | 1 update first data | 1 update first data | +---------------------+---------------------+ 1 row in set (0.00 sec)
注:當(dāng)表被刪除時(shí)爹凹,其關(guān)聯(lián)的觸發(fā)器也都會(huì)自動(dòng)被刪除。
-
-
查看:MySQL 中可以通過如下兩種方式查看觸發(fā)器相關(guān)信息:
-
通過命令
SHOW TRIGGERS
進(jìn)行查看搅方。其語法如下所示:SHOW TRIGGERS [{FROM | IN} db_name] [LIKE 'pattern' | WHERE expr]
其中:
pattern
匹配的是表名,而不是觸發(fā)器名锚沸。 -
在 MySQL 中音比,所有觸發(fā)器的定義都存儲(chǔ)在內(nèi)置數(shù)據(jù)庫
information_schema
中的triggers
表中来屠,因此可以通過查詢?cè)摫韥聿榭从|發(fā)器信息:SELECT * FROM information_schema.triggers WHERE trigger_name = 'trigger_name'
注:同樣的磕仅,如果想查看系統(tǒng)中存在的所有觸發(fā)器名稱,可使用如下命令:
SELECT trigger_name FROM information_schema.triggers;
-
-
刪除:刪除觸發(fā)器使用如下命令:
DROP TRIGGER [IF EXISTS] [schema_name.]trigger_name
其他
MySQL 的內(nèi)容很多,以下記錄一些邊角料內(nèi)容:
-
區(qū)分大小寫搜索:默認(rèn)情況下,字符匹配搜索是不區(qū)分大小寫栖茉。如果想?yún)^(qū)分大小寫隙袁,可以采用如下方式:
SELECT * FROM `table_name` WHERE `column` = CONVERT('value' using utf8mb4) COLLATE utf8mb4_bin;
其中:
-
table_name
:表示要進(jìn)行查詢的表名遍坟。 -
column
:表示字段名怎诫。 -
CONVERT('value' using utf8mb4)
:表示將要查詢的值value
轉(zhuǎn)換為utf8mb4
編碼。 -
COLLATE utf8mb4_bin
:表示排序或比較使用的編碼為utf8mb4_bin
,這里需要注意的是:COLLATE
指定的編碼集必須與數(shù)據(jù)表的編碼集一致吗铐,否則表中的數(shù)據(jù)編碼與指定查詢的編碼不一致,可能會(huì)導(dǎo)致結(jié)果出錯(cuò)。
注:可以通過命令
SHOW COLLATION
查看 MySQL 內(nèi)置的編碼集册养,其中:- 以
_ci
結(jié)尾的編碼集表示大小寫無關(guān)靠闭,即 Case Insensitive 的縮寫。 - 以
_bin
結(jié)尾的編碼集表示將字符串看作二進(jìn)制字符串坎炼,然后從最高位到最低位依次進(jìn)行比對(duì)愧膀,顯然這是一種大小寫區(qū)分的比對(duì)。
更多
COLLATE
詳細(xì)內(nèi)容谣光,可參考:MYSQL中的COLLATE是什么檩淋? -
查看當(dāng)前使用的數(shù)據(jù)庫:
SELECT DATABASE()
-
全局變量持久化:在 MySQL 中,全局變量可以通過
SET GLOBAL
語句進(jìn)行設(shè)置萄金,如下所示:SET GLOBAL MAX_EXECUTION_TIME = 2000
但是
SET GLOBAL
設(shè)置的變量值在數(shù)據(jù)庫重啟后蟀悦,會(huì)失效,如果需要持久化全局變量設(shè)置氧敢,可以使用SET PERSIST
語句日戈,比如:SET PERSIST MAX_EXECUTION_TIME = 2000
注:
SET PERSIST
語句是 MySQL 8.0 提供的新特性。使用
SET PERSIST
命令設(shè)置的配置內(nèi)容會(huì)被保存到數(shù)據(jù)目錄下的mysqld-auto.cnf
配置文件中福稳,MySQL 在啟動(dòng)時(shí)會(huì)讀取該文件涎拉,這樣就能覆蓋默認(rèn)配置文件內(nèi)容。注:可通過以下命令查看系統(tǒng)變量:
SHOW VARIABLES LIKE 'pattern'
附錄
-
Docker 安裝 mysql/mysql-server:其配置步驟如下所示:
-
下載最新鏡像文件:
$ docker pull mysql/mysql-server
-
創(chuàng)建容器啟動(dòng) mysql-server 鏡像:
$ docker run --name mysql --restart on-failure -p 3306:3306 -d mysql/mysql-server
-
查看 MySQL 日志獲取自動(dòng)生成的 ROOT 密碼:
$ docker logs mysql | grep GENERATED --color=auto GENERATED ROOT PASSWORD: Axegh3kAJyDLaRuBemecis&EShOs
-
登錄到容器內(nèi)的 MySQL的圆,輸入上述步驟獲取的密碼:
$ docker exec -it mysql mysql -uroot -p
-
首次登錄成功后鼓拧,進(jìn)行其他操作前,需要手動(dòng)更改 ROOT 密碼:
# 將 root 用戶密碼更改為 password mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'password';
以上越妈,就已完成 mysql-server 的配置季俩。
-