先來一段偽代碼,首先你能看懂麼?
SELECT DISTINCT <select_list>
FROM <left_table>
<join_type> JOIN <right_table>
ON <join_condition>
WHERE <where_condition>
GROUP BY <group_by_list>
HAVING <having_condition>
ORDER BY <order_by_condition>
LIMIT <limit_number>
如果你知道每個關鍵字的意思,作用,如果你還用過的話,那再好不過了。但是,你知道這些語句,它們的執行順序你清楚麼?如果你非常清楚,你就沒有必要再浪費時間繼續閱讀了;如果你不清楚,非常好,你應該慶幸你閱讀到了這麼好的一篇文章。
准備工作
首先聲明下,一切測試操作都是在MySQL數據庫上完成,關於MySQL數據庫的一些簡單操作,請閱讀一下文章:
•《MySQL掃盲篇》
•《MySQL存儲引擎介紹》
•《MySQL數據類型和屬性》
•《MySQL處理數據庫和表的常用命令》
繼續做以下的前期准備工作:
1.新建一個測試數據庫TestDB; create database TestDB;
2.創建測試表table1和table2; CREATE TABLE table1
(
customer_id VARCHAR(10) NOT NULL,
city VARCHAR(10) NOT NULL,
PRIMARY KEY(customer_id)
)ENGINE=INNODB DEFAULT CHARSET=UTF8;
CREATE TABLE table2
(
order_id INT NOT NULL auto_increment,
customer_id VARCHAR(10),
PRIMARY KEY(order_id)
)ENGINE=INNODB DEFAULT CHARSET=UTF8;
3.插入測試數據; INSERT INTO table1(customer_id,city) VALUES('163','hangzhou');
INSERT INTO table1(customer_id,city) VALUES('9you','shanghai');
INSERT INTO table1(customer_id,city) VALUES('tx','hangzhou');
INSERT INTO table1(customer_id,city) VALUES('baidu','hangzhou');
INSERT INTO table2(customer_id) VALUES('163');
INSERT INTO table2(customer_id) VALUES('163');
INSERT INTO table2(customer_id) VALUES('9you');
INSERT INTO table2(customer_id) VALUES('9you');
INSERT INTO table2(customer_id) VALUES('9you');
INSERT INTO table2(customer_id) VALUES('tx');
INSERT INTO table2(customer_id) VALUES(NULL);
准備工作做完以後,table1和table2看起來應該像下面這樣:
mysql> select * from table1;
+-------------+----------+
| customer_id | city |
+-------------+----------+
| 163 | hangzhou |
| 9you | shanghai |
| baidu | hangzhou |
| tx | hangzhou |
+-------------+----------+
4 rows in set (0.00 sec)
mysql> select * from table2;
+----------+-------------+
| order_id | customer_id |
+----------+-------------+
| 1 | 163 |
| 2 | 163 |
| 3 | 9you |
| 4 | 9you |
| 5 | 9you |
| 6 | tx |
| 7 | NULL |
+----------+-------------+
7 rows in set (0.00 sec)
4.准備SQL邏輯查詢測試語句 SELECT a.customer_id, COUNT(b.order_id) as total_orders
FROM table1 AS a
LEFT JOIN table2 AS b
ON a.customer_id = b.customer_id
WHERE a.city = 'hangzhou'
GROUP BY a.customer_id
HAVING count(b.order_id) < 2
ORDER BY total_orders DESC;
使用上述SQL查詢語句來獲得來自杭州,並且訂單數少於2的客戶。
好吧,這些測試表和測試數據均來自《MySQL技術內幕:SQL編程》,這應該不算抄襲吧,借鑒借鑒啊。
萬事俱備,只欠東風。接下來開始這篇文章最正式的部分吧。
SQL邏輯查詢語句執行順序
還記得上面給出的那一長串的SQL邏輯查詢規則麼?那麼,到底哪個先執行,哪個後執行呢?現在,我先給出一個查詢語句的執行順序:
(7) SELECT
(8) DISTINCT <select_list>
(1) FROM <left_table>
(3) <join_type> JOIN <right_table>
(2) ON <join_condition>
(4) WHERE <where_condition>
(5) GROUP BY <group_by_list>
(6) HAVING <having_condition>
(9) ORDER BY <order_by_condition>
(10) LIMIT <limit_number>
上面在每條語句的前面都標明了執行順序號,不要問我怎麼知道這個順序的。我也是讀各種“武林秘籍”才得知的,如果你有功夫,去閱讀一下MySQL的源碼,也會得出這個結果的。
好了,上面我標出了各條查詢規則的執行先後順序,那麼各條查詢語句是如何執行的呢?這就是我今天這篇博文的重點內容。Go on…
執行FROM語句
在這些SQL語句的執行過程中,都會產生一個虛擬表,用來保存SQL語句的執行結果(這是重點),我現在就來跟蹤這個虛擬表的變化,得到最終的查詢結果的過程,來分析整個SQL邏輯查詢的執行順序和過程。
第一步,執行FROM語句。我們首先需要知道最開始從哪個表開始的,這就是FROM告訴我們的。現在有了<left_table>和<right_table>兩個表,我們到底從哪個表開始,還是從兩個表進行某種聯系以後再開始呢?它們之間如何產生聯系呢?——笛卡爾積
關於什麼是笛卡爾積,請自行Google補腦。經過FROM語句對兩個表執行笛卡爾積,會得到一個虛擬表,暫且叫VT1(vitual table 1),內容如下:
+-------------+----------+----------+-------------+
| customer_id | city | order_id | customer_id |
+-------------+----------+----------+-------------+
| 163 | hangzhou | 1 | 163 |
| 9you | shanghai | 1 | 163 |
| baidu | hangzhou | 1 | 163 |
| tx | hangzhou | 1 | 163 |
| 163 | hangzhou | 2 | 163 |
| 9you | shanghai | 2 | 163 |
| baidu | hangzhou | 2 | 163 |
| tx | hangzhou | 2 | 163 |
| 163 | hangzhou | 3 | 9you |
| 9you | shanghai | 3 | 9you |
| baidu | hangzhou | 3 | 9you |
| tx | hangzhou | 3 | 9you |
| 163 | hangzhou | 4 | 9you |
| 9you | shanghai | 4 | 9you |
| baidu | hangzhou | 4 | 9you |
| tx | hangzhou | 4 | 9you |
| 163 | hangzhou | 5 | 9you |
| 9you | shanghai | 5 | 9you |
| baidu | hangzhou | 5 | 9you |
| tx | hangzhou | 5 | 9you |
| 163 | hangzhou | 6 | tx |
| 9you | shanghai | 6 | tx |
| baidu | hangzhou | 6 | tx |
| tx | hangzhou | 6 | tx |
| 163 | hangzhou | 7 | NULL |
| 9you | shanghai | 7 | NULL |
| baidu | hangzhou | 7 | NULL |
| tx | hangzhou | 7 | NULL |
+-------------+----------+----------+-------------+
總共有28(table1的記錄條數 * table2的記錄條數)條記錄。這就是VT1的結果,接下來的操作就在VT1的基礎上進行。
執行ON過濾
執行完笛卡爾積以後,接著就進行ON a.customer_id = b.customer_id條件過濾,根據ON中指定的條件,去掉那些不符合條件的數據,得到VT2表,內容如下:
+-------------+----------+----------+-------------+
| customer_id | city | order_id | customer_id |
+-------------+----------+----------+-------------+
| 163 | hangzhou | 1 | 163 |
| 163 | hangzhou | 2 | 163 |
| 9you | shanghai | 3 | 9you |
| 9you | shanghai | 4 | 9you |
| 9you | shanghai | 5 | 9you |
| tx | hangzhou | 6 | tx |
+-------------+----------+----------+-------------+
VT2就是經過ON條件篩選以後得到的有用數據,而接下來的操作將在VT2的基礎上繼續進行。