一条SQL语句的千回百转

SQL语言相信你们都不陌生,从本质上来讲,它是一种结构化查询语言,是用来数据库之间的通讯的编程语言。做为一名Java程序员,咱们从Java角度来看,SQL语言至关于Java接口,而数据库是实现这个接口的实现类,SQL语句则是实现类的方法!!。从这里咱们就能够理解了,每一个数据库都有着本身独特的规则,但大致上是遵循SQL标准。html

SQL 语句有一个让大部分人都感到困惑的地方,就是我写的 SQL 语句的跟我预想要的结果不同。在这里,咱们就以 Mysql 数据库为例,对一条 SQL 语句的执行顺序进行分析。mysql

首先看一下示例语句程序员

image

它的执行顺序是这样的sql

image

准备数据数据库

咱们会先准备一些数据,即建立 classes、student 表,并插入测试数据, SQL语句以下:编程

DROP TABLE classes;
CREATE TABLE classes (class_id varchar(10), class_name varchar(10)) ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE=utf8_general_ci;
INSERT INTO classes (class_id, class_name) VALUES ('2', '2班');
INSERT INTO classes (class_id, class_name) VALUES ('1', '1班');

DROP TABLE student;
CREATE TABLE student (stu_name varchar(10), class_id varchar(10), stu_id varchar(10)) ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE=utf8_general_ci;

INSERT INTO student (stu_name, class_id, stu_id) VALUES ('王五', '2', '124');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('王五', '2', '123');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('李四', '2', '122');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('李四', '1', '114');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('张三', '1', '112');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('张三', '2', '121');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('李四', '1', '113');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('张三', '1', '111');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('小红', '', '141');
INSERT INTO student (stu_name, class_id, stu_id) VALUES ('王五', '2', '125');

OK,有了数据以后,咱们就能够来看看 SQL 语句在 MySQL 中执行过程了,SQL 语句以下:编程语言

select stu_name as name,count(stu_name) total from student s left join classes c on s.class_id=c.class_id where stu_name in('张三','王五','李四') group by name HAVING count(stu_name)>2 order by stu_id desc limit 1;

SQL执行之旅测试

可能你如今还对 Mysql 语句的执行顺序只知其一;不知其二,不要紧,下来我将按 SQL 执行的顺序详细介绍每一个关键字的做用,以及注意的地方。code

一、FROM:对FROM子句中的前两个表执行笛卡尔积(交叉联接),生成虚拟表VT1。htm

image

二、ON:对VT1应用ON筛选器,只有那些使为真才被插入到VT2。ON不能单独,在这里你能够把ON理解为WHERE。

三、JOIN:若是指定了OUTER JOIN(相对于CROSS JOIN或INNER JOIN),保留表(主表)中不符合ON条件匹配的行将做为外部行添加到VT2,生成VT3。若是FROM子句超过两个表,上一个联接生成的结果表会和下一个表重复执行步骤1到步骤3,直处处理完全部的表的关联。

四、WHERE:对VT3应用WHERE筛选器,只有为true的行才插入VT4。

五、GROUP BY:按GROUP BY子句中的列列表对VT4中的行进行分组,生成VT5。

在这里会有一个奇怪的现象,MySQL执行顺序GROUP BY -> HAVING -> SELECT,从顺序看SELECL在GROUP BY以后,GROUP BY 应该不可使用SELECT字段别名,可是在GROUP BY却可使用SELECT字段别名,主要缘由MySQL扩展了标准SQL,容许GROUP BY子句使用的SELECT子句中的别名以及和非列表表达式等标准, 并认为语句是有效的。
从MySQL 5.7.5开始,默认SQL mode模式包括 ONLY_FULL_GROUP_BY。(在5.7.5以前,MySQL不检测功能依赖性,ONLY_FULL_GROUP_BY默认状况下不启用。更多请参考:MySQL 5.7 Reference Manual-GROUP BY

六、HAVING :对VT5应用HAVING筛选器,只有为true的组插入到VT6。

HAVING同GROUP BY同样,MySQL拓展SQL标准以容许HAVING可使用别名和非列表表达式

七、SELECT:将VT6每一组数据执行select xx,有几组就执行几回,产生VT7。

这里有一点要注意,当SQL mode 模式ONLY_FULL_GROUP_BY不开启,不会强制SELECT指定的字段必须属于GROUP BY后的条件。若符合条件的字段有多个,则只显示第一次出现的字段。虽然这种查询在语法上经过了,但结果并无什么意义,由于其余字段并不是须要的准确值。因此最好select语句指定的字段必须是“分组依据字段”。

八、ORDER BY:将VT7中的行按ORDER BY子句中的列列表顺序,ORDER BY只能选择SELECT的字段

九、LIMIT:从VT7的开始处选择指定数量或比例的行,生成表VT8,并返回给调用者。

OK,到这里就执行结束了。咱们能够发现,SQL 语句的语法顺序和执行顺序并不一致,若是你已经能够清醒知道它们之间差别,你就能够看出为何之前写的SQL老是和咱们预想的不一致。你看,哪怕只有一条小小的 SQL 语句都有这么多门道,只有不断专研探究,咱们才能够真正掌握这一门技术。

这里多提一下,在SQL语法有几点要特别注意,SELECT虽然在GROUP BY和HAVING 以后,可是若是SQL mode模式 ONLY_FULL_GROUP_BY不开启,GROUP BY和HAVING是容许使用SELECT的字段,并且也不会强制SELECT指定的字段必须属于GROUP BY后的条件。至此SQL的解析之旅就结束了,最后用一张图总结一下今天的内容:

image

参考:MySQL 5.7 Reference Manual

相关文章
相关标签/搜索