SQL语言相信大家都不陌生,从本质上来说,它是一种结构化查询语言,是用来数据库之间的通信的编程语言。作为一名Java程序员,我们从Java角度来看,SQL语言相当于Java接口,而数据库是实现这个接口的实现类,SQL语句则是实现类的方法!!。从这里我们就可以理解了,每个数据库都有着自己独特的规则,但大体上是遵循SQL标准。

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

首先看一下示例语句

它的执行顺序是这样的

准备数据

我们会先准备一些数据,即创建 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 执行的顺序详细介绍每个关键字的作用,以及注意的地方。

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

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

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

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

5、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

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

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

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

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

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

9、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的解析之旅就结束了,最后用一张图总结一下今天的内容:

参考:MySQL 5.7 Reference Manual

一条SQL语句的千回百转的更多相关文章

  1. 一条Sql语句分组排序并且限制显示的数据条数

    如果我想得到这样一个结果集:分组排序,并且每组限定记录集的数量,用一条SQL语句能办到吗? 比如说,我想找出学生期末考试中,每科的前3名,并按成绩排序,只用一条SQL语句,该怎么写? 表[TScore ...

  2. JavaWeb 学习009-4个页面,5条sql语句(添加、查看、修改、删除)

    ===========++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++==+++++++++ 2016-12-3------ ...

  3. JavaWeb 学习007-4个页面,5条sql语句(添加、查看、修改、删除)2016-12-2

    需要复习的知识: 关联查询 =================================================================================班级模块学 ...

  4. C#实现执行多条SQl语句,实现数据库事务

    C#实现执行多条SQl语句,实现数据库事务 在数据库中使用事务的好处,相信大家都有听过银行存款的交易作为事务的一个例子.事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的 ...

  5. 如果一条SQL语句太长,我们可以通过回车键来创建一个新行来编写SQL语句,SQL语句的命令结束符为分号(;)。

    1.如果一条SQL语句太长,我们可以通过回车键来创建一个新行来编写SQL语句,SQL语句的命令结束符为分号(;). 2.select查询的多个字段之间要用逗号“,”分割,如果查询涉及多个表,那多个表之 ...

  6. c#中执行多条sql语句【ORA-00911: 无效字符】

    问题描述: 在plsql里执行多条sql语句的时候,使用“,”(逗号)分隔,测试可以执行多条,而在C#执行多条sql语句的时候[ORA-00911: 无效字符]. 有时我们需要一次性执行多条sql语句 ...

  7. Spring 中jdbcTemplate 实现执行多条sql语句

    说一下Spring框架中使用jdbcTemplate实现多条sql语句的执行: 很多情况下我们需要处理一件事情的时候需要对多个表执行多个sql语句,比如淘宝下单时,我们确认付款时要对自己银行账户的表里 ...

  8. MyBatis一次执行多条SQL语句

    MyBatis一次执行多条SQL语句 有个常见的场景:删除用户的时候需要先删除用户的外键关联数据,否则会触发规则报错. 解决办法不外乎有三个:1.多条sql分批执行:2.存储过程或函数调用:3.sql ...

  9. 用一条SQL语句取出第 m 条到第 n 条记录的方法

    原文:用一条SQL语句取出第 m 条到第 n 条记录的方法   --从Table 表中取出第 m 条到第 n 条的记录:(Not In 版本)       *    FROM Table     id ...

随机推荐

  1. ls 操作命令 -l/-R和rm -r dir 功能实现

    ls -R #include <sys/stat.h> #include <dirent.h> #include <fcntl.h> #include <st ...

  2. django的orm操作的补充

    ---恢复内容开始--- 你在建立一个登陆的时候需要用到auth这个用户登陆模块 这个时候就需要用到user表中的信息你就可以继承 这个时候我们的用户表中继承我们user继承的表 ABstractUs ...

  3. maven将依赖的包一起打包

    把以下内容输入到pom中即可 <build> <plugins> <!-- 将项目的依赖包复制到 target/lib --> <plugin> < ...

  4. oracle sql练习 菜鸟入门!

    进入公司 ,首先是进行SQL培训 一下是针对oracle的emp与dept表进行的基础查询 --1.选择部门30中的所有员工: ; --2.列出所有办事员(CLERK)的姓名,编号和部门编号: sel ...

  5. August 18th 2017 Week 33rd Friday

    If you shed tears when you miss the sun, you also miss the stars. 如果你因为错过太阳而哭泣,你也将会错过繁星. If you have ...

  6. Alpha Scrum7

    Alpha Scrum7 牛肉面不要牛肉不要面 Alpha项目冲刺(团队作业5) 各个成员在 Alpha 阶段认领的任务 林志松:项目发布 陈远军.陈彬:播放器各环境的测试 项目的发布说明 本版本的新 ...

  7. UVA 10288 Coupons---概率 && 分数类模板

    题目链接: https://cn.vjudge.net/problem/UVA-10288 题目大意: 一种刮刮卡一共有n种图案,每张可刮出一个图案,收集n种就有奖,问平均情况下买多少张才能中奖?用最 ...

  8. 安装LAMP PHP的./configure 參数,未出现MYSQ

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/default7/article/details/30613781 编译參数: ./configure ...

  9. 【CSS】iconfont的使用

    说到浏览器对@font-face的兼容问题,这里涉及到一个字体format的问题,因为不同的浏览器对字体格式支持是不一致的,这样大家有必要了解一下,各种版本的浏览器支持什么样的字体,前面也简单带到了有 ...

  10. shell脚本进阶 详解及其实例(一)

    v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VM ...