不同于一些编程语言,sql的执行顺序并非按照编码顺序来执行,第一个被处理的子句总是FROM子句,

具体步骤:

准备两个表

CREATE TABLE `customers` (
 `customer_id` varchar(10) NOT NULL,
 `city` varchar(10) NOT NULL,
 PRIMARY KEY (`customer_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `orders` (
 `order_id` int(10) NOT NULL AUTO_INCREMENT,
 `customer_id` varchar(10) DEFAULT NULL,
 PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `test`.`customers` (`customer_id`, `city`) VALUES ('有赞', '杭州 ');
INSERT INTO `test`.`customers` (`customer_id`, `city`) VALUES ('网易', '杭州 ');
INSERT INTO `test`.`customers` (`customer_id`, `city`) VALUES ('腾讯', '深圳 ');
INSERT INTO `test`.`customers` (`customer_id`, `city`) VALUES ('阿里巴巴', '杭州 ');
INSERT INTO `test`.`orders` (`order_id`, `customer_id`) VALUES ('1', '网易 ');
INSERT INTO `test`.`orders` (`order_id`, `customer_id`) VALUES ('2', '网易 ');
INSERT INTO `test`.`orders` (`order_id`, `customer_id`) VALUES ('3', '阿里巴巴 ');
INSERT INTO `test`.`orders` (`order_id`, `customer_id`) VALUES ('4', '腾讯 ');

第一步 执行笛卡尔积

对From子句前后的两张表进行笛卡尔积操作,也称作交叉连接(Cross Join),生产虚拟表VT1。如果FROM子句前的表中包含a行数据,From子句后的表中包含b行数据,那么虚拟表VT1中将包含a*b行数据。(4*4=16)

SELECT
 c.customer_id AS c_customer_id,
 c.city,
 o.order_id,
 o.customer_id AS o_customer_id 
FROM
 customers c
 JOIN orders o

2 应用ON过滤器

sql有三个过滤过程,ON,WHERE,HAVING

ON是最先执行的过滤过程。根据上一个步骤产生的虚拟表VT1,应用ON进行过滤

SELECT
 c.customer_id AS c_customer_id,
 c.city,
 o.order_id,
 o.customer_id AS o_customer_id 
FROM
 customers c
 JOIN orders o ON c.customer_id = o.customer_id

3.添加外部行 

这一步只有在连接类型为OUTER JOIN时才发生,如LEFT OUTER JOIN,RIGHT OUTER JOIN,FULL OUTER JOIN。虽然在大多数时候我们可以省略OUTER关键词,但OUTER代表的就是外部行。LEFT OUTER JOIN把左表记为保留表,RIGHT OUTER JOIN把右表记为保留表,FULL OUTER JOIN把左右表都记为保留表。添加外部行的工作就是在VT2表的基础上添加保留表中被过滤条件过滤掉的数据,非保留表的数据被赋予NULL值,最后生成虚拟表VT3

SELECT
 c.customer_id AS c_customer_id,
 c.city,
 o.order_id,
 o.customer_id AS o_customer_id 
FROM
 customers c
 LEFT JOIN orders o ON c.customer_id = o.customer_id

有赞在VT2表中由于没有订单而被过滤,因此有赞作为外部行被添加到虚拟表VT2中,将非保留表中的数据赋值为NULL

 

如果需要连接表的数量大于2,则对虚拟表VT3重做步骤1-步骤3,最后产生的虚拟表作为下一个步骤的输出

4.应用WEHRE过滤器

对上一个步骤产生的虚拟表VT3进行WHERE条件过滤,只有符合<where_condition>的记录才会输出到虚拟表VT4中。

在当前应用WHERE过滤器时,有两种过滤是不被允许的

  1. 由于数据还没有分组,因此现在还不能再WHERE过滤器中使用where_condition=MIN(col)这类对统计的过滤
  2. 由于没有进行列的选取操作,因此在SELECT中使用列的别名也是不被允许的,如SELECT city as c FROM t WHERE c = "shanghai"是不允许出现的
SELECT
 c.customer_id AS c_customer_id,
 c.city,
 o.order_id,
 o.customer_id AS o_customer_id 
FROM
 customers c
 LEFT JOIN orders o ON c.customer_id = o.customer_id 
WHERE
 c.city = "杭州"

5分组 

根据指定的列对上个步骤中产生的虚拟表进行分组,最后得到虚拟表VT5

SELECT
 c.customer_id AS c_customer_id,
 c.city,
 o.order_id,
 o.customer_id AS o_customer_id 
FROM
 customers c
 LEFT JOIN orders o ON c.customer_id = o.customer_id 
WHERE
 c.city = "杭州" 
GROUP BY
 c.customer_id

 

6应用ROLLUP或CUBE

如果指定了ROLLUP选项,那么将创建一个额外的记录添加到虚拟表VT5的最后,并生成虚拟表VT6。因为我们的查询并未用到ROLLUP,所以将跳过本步骤。

对于CUBE选项,MySQL数据库虽然支持该关键字的解析,但是并未实现该功能。

 

7应用HAVING过滤器

这是最后一个条件过滤器了,之前已经分别应用了ON和WHERE过滤器。在该步骤中对于上一步产生的虚拟表应用HAVING过滤器,HAVING是对分组条件进行过滤的筛序器。

SELECT
 c.customer_id AS c_customer_id,
 c.city,
 o.order_id,
 o.customer_id AS o_customer_id 
FROM
 customers c
 LEFT JOIN orders o ON c.customer_id = o.customer_id 
WHERE
 c.city = "杭州" 
GROUP BY
 c.customer_id 
HAVING
 count( o.customer_id ) < 2

网易订单数两个,被过滤

8.处理SELECT列表 

直到这一步,SELECT才被执行,虽然SELECT是查询中最先被指定的部分,但是直到步骤8时才真正进行处理。在这一步中,将SELECT中指定的列从上一步产生的虚拟表中选出

SELECT
 c.customer_id,
 count( o.order_id ) AS total_orders 
FROM
 customers AS c
 LEFT JOIN orders AS o ON c.customer_id = o.customer_id 
WHERE
 c.city = "杭州" 
GROUP BY
 c.customer_id 
HAVING
 count( o.order_id ) < 2

9.应用DISTINCT子句 ,返回唯一不同的值

如果在查询中指定了DISTINCT子句,则会创建一张内存临时表(如果内存中存放不下就放到磁盘上)。这张内存临时表的表结构和上一步产生的虚拟表一样,不同的是对进行DISTINCT操作的列增加了一个唯一索引,以此来去除重复数据。

由于在这个SQL查询中未指定DISTINCT,因此跳过本步骤。另外对使用了GROUP BY的查询,再使用DISTINCT是多余的,因为已经进行分组,不会移除任何行

 

10.用ORDER BY子句

根据ORDER BY子句中指定的列对上一个输出的虚拟表进行排列,返回新的虚拟表。

SELECT
 c.customer_id,
 count( o.order_id ) AS total_orders 
FROM
 customers AS c
 LEFT JOIN orders AS o ON c.customer_id = o.customer_id 
WHERE
 c.city = "杭州" 
GROUP BY
 c.customer_id 
HAVING
 count( o.order_id ) < 2 
ORDER BY
 total_orders DESC

 

11.LIMIT子句

在该步骤中应用LIMIT子句,从上一步骤的虚拟表选出从指定位置开始的指定行数据。对于没有应用ORDER BY的LIMIT子句,结果同样可能是无序的,因此LIMIT子句通常和ORDER BY子句一起使用