架构最好看图,再配上必要的说明文字。
下图根据参考书籍中一图为原本,再在其上添加上了自己的理解。
从上图中我们可以看到,整个架构分为两层,上层是MySQLD的被称为的‘SQL Layer’,下层是各种各样对上提供接口的存储引擎,被称为‘Storage Engine Layer’。其它各个模块和组件,从名字上就可以简单了解到它们的作用,这里就不再累述了。
下面再向前走一些,容我根据自己的认识说一下查询执行的流程是怎样的:
一图小总结
接下来再走一步,让我们看看一条SQL语句的前世今生。
首先看一下示例语句
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 >
然而它的执行顺序是这样的
FROM <left_table>
ON <join_condition>
<join_type> JOIN <right_table>
WHERE <where_condition>
GROUP BY <group_by_list>
HAVING <having_condition>
SELECT
DISTINCT <select_list>
ORDER BY <order_by_condition>
LIMIT <limit_number>
虽然自己没想到是这样的&#xff0c;不过一看还是很自然和谐的&#xff0c;从哪里获取&#xff0c;不断的过滤条件&#xff0c;要选择一样或不一样的&#xff0c;排好序&#xff0c;那才知道要取前几条呢。既然如此了&#xff0c;那就让我们一步步来看看其中的细节吧。
准备工作
1.创建测试数据库
create database testQuery
2.创建测试表
CREATE TABLE table1
(uid VARCHAR(10) NOT NULL,name VARCHAR(10) NOT NULL,PRIMARY KEY(uid)
)ENGINE&#61;INNODB DEFAULT CHARSET&#61;UTF8;CREATE TABLE table2
(oid INT NOT NULL auto_increment,uid VARCHAR(10),PRIMARY KEY(oid)
)ENGINE&#61;INNODB DEFAULT CHARSET&#61;UTF8;
3.插入数据
INSERT INTO table1(uid,name) VALUES(&#39;aaa&#39;,&#39;mike&#39;),(&#39;bbb&#39;,&#39;jack&#39;),(&#39;ccc&#39;,&#39;mike&#39;),(&#39;ddd&#39;,&#39;mike&#39;);
INSERT INTO table2(uid) VALUES(&#39;aaa&#39;),(&#39;aaa&#39;),(&#39;bbb&#39;),(&#39;bbb&#39;),(&#39;bbb&#39;),(&#39;ccc&#39;),(NULL);
4.最后想要的结果
SELECTa.uid,count(b.oid) AS total
FROMtable1 AS a
LEFT JOIN table2 AS b ON a.uid &#61; b.uid
WHEREa. NAME &#61; &#39;mike&#39;
GROUP BYa.uid
HAVINGcount(b.oid) < 2
ORDER BYtotal DESC
LIMIT 1;
&#xff01;现在开始SQL解析之旅吧&#xff01;
mysql> select * from table1,table2;
&#43;-----&#43;------&#43;-----&#43;------&#43;
| uid | name | oid | uid |
&#43;-----&#43;------&#43;-----&#43;------&#43;
| aaa | mike | 1 | aaa |
| bbb | jack | 1 | aaa |
| ccc | mike | 1 | aaa |
| ddd | mike | 1 | aaa |
| aaa | mike | 2 | aaa |
| bbb | jack | 2 | aaa |
| ccc | mike | 2 | aaa |
| ddd | mike | 2 | aaa |
| aaa | mike | 3 | bbb |
| bbb | jack | 3 | bbb |
| ccc | mike | 3 | bbb |
| ddd | mike | 3 | bbb |
| aaa | mike | 4 | bbb |
| bbb | jack | 4 | bbb |
| ccc | mike | 4 | bbb |
| ddd | mike | 4 | bbb |
| aaa | mike | 5 | bbb |
| bbb | jack | 5 | bbb |
| ccc | mike | 5 | bbb |
| ddd | mike | 5 | bbb |
| aaa | mike | 6 | ccc |
| bbb | jack | 6 | ccc |
| ccc | mike | 6 | ccc |
| ddd | mike | 6 | ccc |
| aaa | mike | 7 | NULL |
| bbb | jack | 7 | NULL |
| ccc | mike | 7 | NULL |
| ddd | mike | 7 | NULL |
&#43;-----&#43;------&#43;-----&#43;------&#43;
rows in set (0.00 sec)
(1-J2)ON过滤
基于虚拟表VT1-J1这一个虚拟表进行过滤&#xff0c;过滤出所有满足ON 谓词条件的列&#xff0c;生成虚拟表VT1-J2。
注意&#xff1a;这里因为语法限制&#xff0c;使用了’WHERE’代替&#xff0c;从中读者也可以感受到两者之间微妙的关系&#xff1b;
mysql> SELECT-> *-> FROM-> table1,-> table2-> WHERE-> table1.uid &#61; table2.uid-> ;
&#43;-----&#43;------&#43;-----&#43;------&#43;
| uid | name | oid | uid |
&#43;-----&#43;------&#43;-----&#43;------&#43;
| aaa | mike | 1 | aaa |
| aaa | mike | 2 | aaa |
| bbb | jack | 3 | bbb |
| bbb | jack | 4 | bbb |
| bbb | jack | 5 | bbb |
| ccc | mike | 6 | ccc |
&#43;-----&#43;------&#43;-----&#43;------&#43;
rows in set (0.00 sec)
(1-J3)添加外部列
如果使用了外连接(LEFT,RIGHT,FULL)&#xff0c;主表&#xff08;保留表&#xff09;中的不符合ON条件的列也会被加入到VT1-J2中&#xff0c;作为外部行&#xff0c;生成虚拟表VT1-J3。
mysql> SELECT-> *-> FROM-> table1 AS a-> LEFT OUTER JOIN table2 AS b ON a.uid &#61; b.uid;
&#43;-----&#43;------&#43;------&#43;------&#43;
| uid | name | oid | uid |
&#43;-----&#43;------&#43;------&#43;------&#43;
| aaa | mike | 1 | aaa |
| aaa | mike | 2 | aaa |
| bbb | jack | 3 | bbb |
| bbb | jack | 4 | bbb |
| bbb | jack | 5 | bbb |
| ccc | mike | 6 | ccc |
| ddd | mike | NULL | NULL |
&#43;-----&#43;------&#43;------&#43;------&#43;
rows in set (0.00 sec)
下面从网上找到一张很形象的关于‘SQL JOINS’的解释图&#xff0c;如若侵犯了你的权益&#xff0c;请劳烦告知删除&#xff0c;谢谢。
2. WHERE
对VT1过程中生成的临时表进行过滤&#xff0c;满足WHERE子句的列被插入到VT2表中。
注意&#xff1a;
此时因为分组&#xff0c;不能使用聚合运算&#xff1b;也不能使用SELECT中创建的别名&#xff1b;
与ON的区别&#xff1a;
如果有外部列&#xff0c;ON针对过滤的是关联表&#xff0c;主表&#xff08;保留表&#xff09;会返回所有的列&#xff1b;
如果没有添加外部列&#xff0c;两者的效果是一样的&#xff1b;
应用&#xff1a;
对主表的过滤应该放在WHERE&#xff1b;
对于关联表&#xff0c;先条件查询后连接则用ON&#xff0c;先连接后条件查询则用WHERE&#xff1b;
mysql> SELECT-> *-> FROM-> table1 AS a-> LEFT OUTER JOIN table2 AS b ON a.uid &#61; b.uid-> WHERE-> a. NAME &#61; &#39;mike&#39;;
&#43;-----&#43;------&#43;------&#43;------&#43;
| uid | name | oid | uid |
&#43;-----&#43;------&#43;------&#43;------&#43;
| aaa | mike | 1 | aaa |
| aaa | mike | 2 | aaa |
| ccc | mike | 6 | ccc |
| ddd | mike | NULL | NULL |
&#43;-----&#43;------&#43;------&#43;------&#43;
rows in set (0.00 sec)
注意&#xff1a;
原因&#xff1a;
mysql> SELECT-> *-> FROM-> table1 AS a-> LEFT OUTER JOIN table2 AS b ON a.uid &#61; b.uid-> WHERE-> a. NAME &#61; &#39;mike&#39;-> GROUP BY-> a.uid;
&#43;-----&#43;------&#43;------&#43;------&#43;
| uid | name | oid | uid |
&#43;-----&#43;------&#43;------&#43;------&#43;
| aaa | mike | 1 | aaa |
| ccc | mike | 6 | ccc |
| ddd | mike | NULL | NULL |
&#43;-----&#43;------&#43;------&#43;------&#43;
rows in set (0.00 sec)
mysql> SELECT-> *-> FROM-> table1 AS a-> LEFT OUTER JOIN table2 AS b ON a.uid &#61; b.uid-> WHERE-> a. NAME &#61; &#39;mike&#39;-> GROUP BY-> a.uid-> HAVING-> count(b.oid) < 2;
&#43;-----&#43;------&#43;------&#43;------&#43;
| uid | name | oid | uid |
&#43;-----&#43;------&#43;------&#43;------&#43;
| ccc | mike | 6 | ccc |
| ddd | mike | NULL | NULL |
&#43;-----&#43;------&#43;------&#43;------&#43;
rows in set (0.00 sec)
mysql> SELECT-> a.uid,-> count(b.oid) AS total-> FROM-> table1 AS a-> LEFT OUTER JOIN table2 AS b ON a.uid &#61; b.uid-> WHERE-> a. NAME &#61; &#39;mike&#39;-> GROUP BY-> a.uid-> HAVING-> count(b.oid) < 2;
&#43;-----&#43;-------&#43;
| uid | total |
&#43;-----&#43;-------&#43;
| ccc | 1 |
| ddd | 0 |
&#43;-----&#43;-------&#43;
rows in set (0.00 sec)
注意&#xff1a;
mysql> SELECT-> a.uid,-> count(b.oid) AS total-> FROM-> table1 AS a-> LEFT OUTER JOIN table2 AS b ON a.uid &#61; b.uid-> WHERE-> a. NAME &#61; &#39;mike&#39;-> GROUP BY-> a.uid-> HAVING-> count(b.oid) < 2-> ORDER BY-> total DESC;
&#43;-----&#43;-------&#43;
| uid | total |
&#43;-----&#43;-------&#43;
| ccc | 1 |
| ddd | 0 |
&#43;-----&#43;-------&#43;
rows in set (0.00 sec)
注意&#xff1a;
当偏移量很大时效率是很低的&#xff0c;可以这么做&#xff1a;
mysql> SELECT-> a.uid,-> count(b.oid) AS total-> FROM-> table1 AS a-> LEFT JOIN table2 AS b ON a.uid &#61; b.uid-> WHERE-> a. NAME &#61; &#39;mike&#39;-> GROUP BY-> a.uid-> HAVING-> count(b.oid) < 2-> ORDER BY-> total DESC-> LIMIT 1;
&#43;-----&#43;-------&#43;
| uid | total |
&#43;-----&#43;-------&#43;
| ccc | 1 |
&#43;-----&#43;-------&#43;
row in set (0.00 sec)
至此SQL的解析之旅就结束了&#xff0c;上图总结一下&#xff1a;