MySQL入門系列:檢視

小孩子4919發表於2019-03-18

上集回顧

上集嘮叨了表中資料操作的一些語句,包括用於插入資料的INSERT語句,用於刪除資料的DELETE語句,用於更新資料的UPDATE語句。不過我們之前說到的增刪改查的語句都是一次性的,這些請求被包裝成請求被客戶端發到伺服器,伺服器處理後把結果返回給客戶端之後就沒有用了,本集將聚焦於如何讓這些一次性的語句變得可以很容易的被重複使用。

檢視

我們之前嘮叨過連線表的查詢,比方說下邊這個:

mysql> SELECT s1.number, s1.name, s1.major, s2.subject, s2.score FROM student_info AS s1 INNER JOIN student_score AS s2 WHERE s1.number = s2.number AND s1.sex = '男';
+----------+-----------+--------------------------+-----------------------------+-------+
| number   | name      | major                    | subject                     | score |
+----------+-----------+--------------------------+-----------------------------+-------+
| 20180101 | 杜子騰    | 電腦科學與工程         | 母豬的產後護理              |    78 |
| 20180101 | 杜子騰    | 電腦科學與工程         | 論薩達姆的戰爭準備          |    88 |
| 20180103 | 範統      | 軟體工程                 | 母豬的產後護理              |    59 |
| 20180103 | 範統      | 軟體工程                 | 論薩達姆的戰爭準備          |    61 |
+----------+-----------+--------------------------+-----------------------------+-------+
4 rows in set (0.00 sec)

mysql>
複製程式碼

我們查詢出了一些男學生的基本資訊和成績資訊,如果下次還想得到這些資訊,我們就不得不把這個又臭又長的查詢語句再敲一遍,所以MySQL提供了檢視(英文名VIEW)來幫助我們用很容易的方式去複用這些查詢語句。

建立檢視

一個檢視可以理解為一個查詢語句的別名,建立檢視的語句如下:

CREATE VIEW 檢視名 AS 查詢語句
複製程式碼

比如我們想根據上邊那個又臭又長的查詢語句來建立一個檢視可以這麼寫:

mysql> CREATE VIEW male_student_info AS SELECT s1.number, s1.name, s1.major, s2.subject, s2.score FROM student_info AS s1 INNER JOIN student_score AS s2 WHERE s1.number = s2.number AND s1.sex = '男';
Query OK, 0 rows affected (0.02 sec)

mysql>
複製程式碼

這樣,這個名稱為male_student_info的檢視就代表了那一串又臭又長的查詢語句了。

使用檢視

檢視也被稱為虛擬表,因為我們可以對檢視進行一些類似表的增刪改查操作,只不過我們對檢視的相關操作都會被對映到那個又臭又長的查詢語句對應的底層的表上。那一串又臭又長的查詢語句的查詢列表可以被當作檢視虛擬列,比方說male_student_info這個檢視對應的查詢語句中的查詢列表是numbernamemajorsubjectscore,它們也是male_student_info檢視的虛擬列

比如我們可以使用平常的查詢語句從檢視中查詢我們需要的資訊可以這麼寫:

mysql> SELECT * FROM male_student_info;
+----------+-----------+--------------------------+-----------------------------+-------+
| number   | name      | major                    | subject                     | score |
+----------+-----------+--------------------------+-----------------------------+-------+
| 20180101 | 杜子騰    | 電腦科學與工程         | 母豬的產後護理              |    78 |
| 20180101 | 杜子騰    | 電腦科學與工程         | 論薩達姆的戰爭準備          |    88 |
| 20180103 | 範統      | 軟體工程                 | 母豬的產後護理              |    59 |
| 20180103 | 範統      | 軟體工程                 | 論薩達姆的戰爭準備          |    61 |
+----------+-----------+--------------------------+-----------------------------+-------+
4 rows in set (0.00 sec)

mysql>
複製程式碼

這裡我們的查詢列表是*,這也就意味著male_student_info所代表的查詢語句的結果集將作為整個查詢的結果集返回。從這個例子中我們也可以看到,我們不再需要使用那句又臭又長的連線查詢語句了,只需要從它對應的檢視中查詢即可。

除此之外,我們在真實表中使用的那些查詢語句都可以被用到檢視這個虛擬表中,比方說這個查詢語句:

mysql> SELECT subject, AVG(score) FROM male_student_info WHERE score > 60 GROUP BY subject HAVING AVG(score) > 75 LIMIT 1;
+-----------------------+------------+
| subject               | AVG(score) |
+-----------------------+------------+
| 母豬的產後護理        |    78.0000 |
+-----------------------+------------+
1 row in set (0.00 sec)

mysql>
複製程式碼

我們再次強調一遍,檢視其實就是某個查詢的別名,而不是某個查詢的結果集,換句話說就是,建立檢視的時候並不會把那個又臭又長的查詢語句的結果集維護在硬碟或者記憶體裡!在對檢視進行查詢時,MySQL伺服器將會幫助我們把對檢視的查詢語句轉換為對底層表的查詢語句然後再執行,所以上邊這個查詢其實會被轉換成下邊這個查詢語句去執行:

SELECT subject, AVG(score) FROM student_info AS s1 INNER JOIN student_score AS s2 WHERE s1.number = s2.number AND s1.sex = '男' AND score > 60 GROUP BY subject HAVING AVG(score) > 75;
複製程式碼

只不過這個轉換的過程我們並不能看到,所以主觀上認為硬碟或記憶體裡真的維護了一個檢視對應的表而已~ 更復雜的一些查詢語句,比如子查詢、連線查詢什麼的,都可以被用到檢視上,我們這裡就不舉例子了。

有一點比較有趣的是,在查詢時,檢視可以和表一起使用,包括子查詢和連線查詢,比如這樣:

mysql> SELECT * FROM male_student_info WHERE number IN (SELECT number FROM student_info WHERE major = '電腦科學與工程');
+----------+-----------+--------------------------+-----------------------------+-------+
| number   | name      | major                    | subject                     | score |
+----------+-----------+--------------------------+-----------------------------+-------+
| 20180101 | 杜子騰    | 電腦科學與工程         | 母豬的產後護理              |    78 |
| 20180101 | 杜子騰    | 電腦科學與工程         | 論薩達姆的戰爭準備          |    88 |
+----------+-----------+--------------------------+-----------------------------+-------+
2 rows in set (0.00 sec)

mysql>
複製程式碼

所以在使用層面,我們完全可以把檢視當作一個表去使用,但是它的實現原理卻是在執行語句時轉換為對底層表的操作。使用檢視的好處也是顯而易見的,我們可以複用某個查詢語句,從而簡化了查詢操作,避免了每次查詢時都要寫一遍又臭又長的語句;對檢視的操作更加直觀,而不用考慮它底層的查詢細節

利用檢視來建立新檢視

我們前邊說檢視是某個查詢語句的別名,其實這個查詢語句不僅可以從普通的表中查詢資料,也可以從另一個檢視中查詢資料,只要是個合法的查詢語句就好了。比方說我們利用male_student_info檢視來建立另一個新檢視可以這麼寫:

mysql> CREATE VIEW by_view AS SELECT number, name, score FROM male_student_info;
Query OK, 0 rows affected (0.02 sec)

mysql>
複製程式碼

我們查詢一下這個從另一個檢視中生成的檢視:

mysql> SELECT * FROM by_view;
+----------+-----------+-------+
| number   | name      | score |
+----------+-----------+-------+
| 20180101 | 杜子騰    |    78 |
| 20180101 | 杜子騰    |    88 |
| 20180103 | 範統      |    59 |
| 20180103 | 範統      |    61 |
+----------+-----------+-------+
4 rows in set (0.00 sec)

mysql>
複製程式碼

這種利用其他的檢視來生成的新檢視也被稱為巢狀檢視,在對某個巢狀檢視執行查詢時,查詢語句會先被轉換成對它依賴的檢視的查詢,再轉換成對底層表的查詢。

建立檢視時指定自定義列名

我們前邊說過檢視虛擬列其實是這個檢視對應的查詢語句的查詢列表,我們也可以在建立列表的時候為這些虛擬列自定義列名,這些自定義列名寫到檢視名後邊,用逗號,分隔就好了,不過需要注意的是,自定義列名一定要和查詢列表中的查詢物件一一對應。比如我們新建立一個自定義列名的檢視:

mysql> CREATE VIEW student_info_view(no, n, m) AS SELECT number, name, major FROM student_info;
Query OK, 0 rows affected (0.02 sec)

mysql>
複製程式碼

我們的自定義列名列表是no, n, m,分別對應查詢列表中的number, name, major。有了自定義列名之後,我們之後對檢視的查詢語句都要基於這些自定義列名,比如我們可以這麼查詢:

mysql> SELECT no, n, m FROM student_info_view;
+----------+-----------+--------------------------+
| no       | n         | m                        |
+----------+-----------+--------------------------+
| 20180101 | 杜子騰    | 電腦科學與工程         |
| 20180102 | 杜琦燕    | 電腦科學與工程         |
| 20180103 | 範統      | 軟體工程                 |
| 20180104 | 史珍香    | 軟體工程                 |
| 20180105 | 範劍      | 飛行器設計               |
| 20180106 | 朱逸群    | 電子資訊                 |
+----------+-----------+--------------------------+
6 rows in set (0.00 sec)

mysql>
複製程式碼

如果仍舊使用與檢視對應的查詢語句的查詢列表中的列名就會報錯,比如這樣:

mysql> SELECT number, name, major FROM student_info_view;
ERROR 1054 (42S22): Unknown column 'number' in 'field list'
mysql>
複製程式碼

檢視和刪除檢視

檢視有哪些檢視

我們想檢視當前資料庫中有哪些檢視的話,其實和檢視有哪些表的命令是一樣的:

mysql> SHOW TABLES;
+---------------------+
| Tables_in_xiaohaizi |
+---------------------+
| by_view             |
| first_table         |
| male_student_info   |
| second_table        |
| student_info        |
| student_info_view   |
| student_score       |
| t                   |
| t1                  |
| t2                  |
| t3                  |
| zero_table          |
+---------------------+
12 rows in set (0.00 sec)

mysql>
複製程式碼

可以看到,我們建立的幾個檢視,包括by_viewmale_student_infostudent_info_view就都顯示出來了。需要注意的是,因為檢視是一張虛擬表,所以新建立的檢視的名稱不能和當前資料庫中的其他檢視或者表的名稱衝突

檢視檢視的定義

因為檢視是一張虛擬表,所以用來檢視錶結構的語句都可以用來檢視檢視的結構,不過我們經常使用的檢視檢視定義語句是這個:

SHOW CREATE VIEW 檢視名
複製程式碼

我們來檢視一下student_info_view檢視的定義:

mysql> SHOW CREATE VIEW student_info_view\G
*************************** 1. row ***************************
                View: student_info_view
         Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `student_info_view` AS select `student_info`.`number` AS `no`,`student_info`.`name` AS `n`,`student_info`.`major` AS `m` from `student_info`
character_set_client: utf8
collation_connection: utf8_general_ci
1 row in set (0.00 sec)

mysql>
複製程式碼

刪除檢視

如果某個檢視我們不想要了,可以使用這個語句來刪除掉它:

DROP VIEW 檢視名
複製程式碼

比如我們把by_view檢視刪掉可以這麼寫:

mysql> DROP VIEW by_view;
Query OK, 0 rows affected (0.00 sec)

mysql>
複製程式碼

然後再檢視當前資料庫中的表:

mysql> SHOW TABLES;
+---------------------+
| Tables_in_xiaohaizi |
+---------------------+
| first_table         |
| male_student_info   |
| second_table        |
| student_info        |
| student_info_view   |
| student_score       |
| t                   |
| t1                  |
| t2                  |
| t3                  |
| zero_table          |
+---------------------+
11 rows in set (0.00 sec)

mysql>
複製程式碼

這個檢視就不見了!

更新檢視

我們前邊進行的都是對檢視的查詢操作,其實也可以對檢視進行更新,也就是在檢視上執行INSERTDELETEUPDATE語句。對檢視執行更新語句的本質上是對該檢視對應的底層表進行更新。比方說檢視student_info_view的底層表是student_info,所以如果我們對student_info_view執行更新語句就相當於對student_info表進行更新,比方說我們執行這個語句:

mysql> UPDATE student_info_view SET n = '111' WHERE no = 20180101;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql>
複製程式碼

我們再到student_info表中看一下這個學生的名稱是否被改了:

mysql> SELECT name FROM student_info WHERE number = 20180101;
+------+
| name |
+------+
| 111  |
+------+
1 row in set (0.00 sec)

mysql>
複製程式碼

名稱的確被更改成功了!

不過並不是可以在所有的檢視上執行更新語句的,在生成檢視的時候使用了下邊這些語句的都不能進行更新:

  • 分組
  • 連線查詢
  • 子查詢
  • 組合查詢
  • 聚集函式
  • DISTINCT
  • 查詢列表上是非列名的表示式

雖然有這麼多限制,但是需要我們注意的是,一般情況下,我們只在檢視上執行查詢操作而不進行更新操作!這裡介紹對檢視的更新只是為了語法的完整性,並不是建議大家在實際使用過程中使用對檢視的更新功能。

小冊

本系列專欄都是MySQL入門知識,想看進階知識可以到小冊中檢視:《MySQL是怎樣執行的:從根兒上理解MySQL》的連結 。小冊的內容主要是從小白的角度出發,用比較通俗的語言講解關於MySQL進階的一些核心概念,比如記錄、索引、頁面、表空間、查詢優化、事務和鎖等,總共的字數大約是三四十萬字,配有上百幅原創插圖。主要是想降低普通程式設計師學習MySQL進階的難度,讓學習曲線更平滑一點~

MySQL入門系列:檢視

相關文章