MySQL 學(xué)習(xí)筆記(三)

索引

參考文章: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í)行語句伪嫁。
    • DEFINERSQL 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è)視圖:

    1. 為表comment創(chuàng)建一個(gè)視圖,用于操作其字段content
    2. 為表comment創(chuàng)建一個(gè)視圖蛛芥,視圖只有一個(gè)字段comment提鸟,用于操作comment表中的字段content脆淹。
    3. 以表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ì)于表的查詢操作(比如:DESCSHOW 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 VIEWCREATE 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_bodyBEGINEND支持多重嵌套溢谤,其中每條 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 SQLREADS 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)提供翁都,在整個(gè) MySQL 服務(wù)器內(nèi)都有效,其語法如下所示:

        @@global.val_name
        

        其中:

        • 查看系統(tǒng)所有全局變量:可使用如下命令:
          SHOW GLOBAL VARIABLES [like_or_where]
          
        • 查看指定全局變量值:可使用如下命令:
          SELECT @@global.val_name
          
      • 持久化變量:與全局變量一樣菲驴,持久化變量全局有效荐吵。其語法如下所示:

        @@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í)行ELSEIFELSE語句却嗡。

        舉個(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:表示匹配所有除SQLWARNINGNOT 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)類型言询,其中:0false,非0true辆憔。

更多存儲(chǔ)過程內(nèi)容熊榛,可以參考:

觸發(fā)器

觸發(fā)器(trigger)是一個(gè)特殊的存儲(chǔ)過程程序,它預(yù)先定義了六個(gè)事件,分別為BEFORE INSERTAFTER INSERT帖世、BEFORE UPDATEAFTER UPDATE哪轿、BEFORE DELETEAFTER DELETE,當(dāng) MYSQL 對(duì)表執(zhí)行INSERT飘痛、UPDATEDELETE操作時(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ī),其值可為BEFOREAFTER二選一。
    • trigger_event:表示觸發(fā)事件毯盈,包括INSERTUPDATEDELETE可選。
    • 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_timetrigger_event兩兩結(jié)合嗤疯,總共可生成以下六種事件:

    • BEFORE INSERT/AFTER INESRT:在INSERT觸發(fā)器代碼內(nèi)辟汰,可引用一個(gè)名為NEW的虛擬表凑术,該表內(nèi)容為要進(jìn)行插入的數(shù)據(jù)扶踊。
      :對(duì)于AUTO_INCREMENT列,NEWbefore 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è)名為NEWOLD的虛擬表椒涯,NEW表中包含更新的數(shù)據(jù),而OLD表中包含舊的數(shù)據(jù)(暫未被更新的數(shù)據(jù))回梧。
      :在BEFORE UPDATE事件中废岂,可通過更改NEW表中的相關(guān)字段來更改插入的值。

    舉個(gè)例子:創(chuàng)建一個(gè)表tmp,為其添加所有的觸發(fā)器摘仅,分別為表執(zhí)行INSERT掏击、UPDATEDELETE操作滚粟,查看相應(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:其配置步驟如下所示:

    1. 下載最新鏡像文件:

      $ docker pull mysql/mysql-server
      
    2. 創(chuàng)建容器啟動(dòng) mysql-server 鏡像:

      $ docker run --name mysql --restart on-failure -p 3306:3306 -d  mysql/mysql-server
      
    3. 查看 MySQL 日志獲取自動(dòng)生成的 ROOT 密碼:

      $ docker logs mysql | grep GENERATED --color=auto
      GENERATED ROOT PASSWORD: Axegh3kAJyDLaRuBemecis&EShOs
      
    4. 登錄到容器內(nèi)的 MySQL的圆,輸入上述步驟獲取的密碼:

      $ docker exec -it mysql mysql -uroot -p
      
    5. 首次登錄成功后鼓拧,進(jìn)行其他操作前,需要手動(dòng)更改 ROOT 密碼:

      # 將 root 用戶密碼更改為 password
      mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'password';
      

    以上越妈,就已完成 mysql-server 的配置季俩。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末梅掠,一起剝皮案震驚了整個(gè)濱河市酌住,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌阎抒,老刑警劉巖酪我,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件势就,死亡現(xiàn)場(chǎng)離奇詭異遂庄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)吁伺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門逞带,熙熙樓的掌柜王于貴愁眉苦臉地迎上來欺矫,“玉大人,你說我怎么就攤上這事展氓∧屡浚” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵遇汞,是天一觀的道長(zhǎng)未妹。 經(jīng)常有香客問我,道長(zhǎng)勺疼,這世上最難降的妖魔是什么教寂? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮执庐,結(jié)果婚禮上酪耕,老公的妹妹穿的比我還像新娘。我一直安慰自己轨淌,他們只是感情好迂烁,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著递鹉,像睡著了一般盟步。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上躏结,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天却盘,我揣著相機(jī)與錄音,去河邊找鬼。 笑死黄橘,一個(gè)胖子當(dāng)著我的面吹牛兆览,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播塞关,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼抬探,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了帆赢?” 一聲冷哼從身側(cè)響起小压,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎椰于,沒想到半個(gè)月后怠益,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瘾婿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年溉痢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片憋他。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡孩饼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出竹挡,到底是詐尸還是另有隱情镀娶,我是刑警寧澤,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布揪罕,位于F島的核電站梯码,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏好啰。R本人自食惡果不足惜轩娶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望框往。 院中可真熱鬧鳄抒,春花似錦、人聲如沸椰弊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秉版。三九已至贤重,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間清焕,已是汗流浹背并蝗。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國打工祭犯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人滚停。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓盹憎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親铐刘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容