查询的原理

在一个查询中常包含下述子句:

1、select,2、distinct,3、join,4、on,5、from,6、where,7、having,8、group by,9、order by,10、limit

在查询执行过程中,每个子句按照一定的顺序被执行,每个子句被执行时都会产生一张虚拟表,只有最后一步生成的虚拟表才会返回给用户。

我们用实际的例子来讲解下查询的执行过程,先准备以下两张表:

create table t_student(

id bigint(20) not null auto_increment comment '主键ID',

name varchar(10) not null default '' comment '姓名',

age tinyint(3) not null default 0 comment '年龄',

primary key (id)

)engine=innodb default charset=utf8 comment '学生信息表';

create table t_grade(

id bigint(20) not null auto_increment comment '主键ID',

student_id bigint(20) not null default 0 comment '学生ID',

level varchar(4) comment '水平:Null.未评级、A.优秀、B.中等、C.不及格',

primary key (id)

)engine=innodb default charset=utf8 comment '成绩表';

并插入一些基本数据:

insert into t_student(name,age) value('小A',16),('小B',17),('小C',18),('小D',17),('小E',18);

insert into t_grade(student_id,level) value(1,'A'),(2,'B'),(3,'C'),(4,'B'),(5,'Null');

现有一个查询需求“查出是哪些水平拥有至少2个以上且年龄大于等于17岁的学生,并按人数由少到多排序”,那么根据这个需求我们可以写出如下代码:

select g.level, count(g.student_id) as total_student from t_student s left join t_grade g on s.id=g.student_id

where s.age>=17 group by g.level having count(g.student_id) >= 2 order by total_student asc;

下面我们就来说说上述SQL的执行顺序,

1)在一个查询语句中,from子句总是最先执行的,form操作会得到关联表的笛卡尔积,如下图

2)on子句会进行筛选操作,只有符合on后条件的行才会被筛选出来,我们刚刚所写查询的on后条件是s.id=g.student_id,那么不满足这个条件的行就会被去除,如下图有红色小三角标记的是会被保留的行

3)join子句如果是left join或right join,那么保留表(由于我们在SQL中使用的是left join,因此这里的保留表指的是t_student)中未匹配的行(因on子句被去掉的行)会作为外部行添加到虚拟表中,如下图

从上图对于join子句的表现不够直观,我们再向t_student表中插入一条数据,且这条数据是没有与t_grade表有关联关系的,insert into t_student(name,age) value('小F',16);

再看如下截图,由于刚插入的那条数据没有与t_grade表有关联关系,原本是要被on子句被去掉的,但是由于使用了left join,会保留左表的所有数据,同时右表没有与之关联的列值被赋值为Null,这就是添加外部行(left join的全写是left outer join,这里的outer就是指外部行)。

4)接着where子句会对得到的行再进行过滤,只有满足where后条件的行才会被筛选出来,见下图

这里补充讲解一点,对于left join或right join,在on过滤完之后会添加保留表中被on过滤掉的行,而被where过滤的行则是永久性过滤。

5)group by对经过了where条件筛选后的数据进行分组;

6)having需要和group by配合使用,因为having使用的前提是group by已经对数据完成了分组,而having就是来对分组后的数据再进行筛选,如下图

需要注意的是,若在left join或right join查询中,在select子句后使用count(1)或count(*),可能会把添加的外部行统计入内,从而导致查询结果与预期结果不符,对于这样的查询最好是使用count(具体列名)。

7)select将需要返回的列筛选出来,我们可以看到select的优先级并不高;

8)如果查询语句中带有去重子句distinct,则会执行去掉重复记录的操作;

去掉重复记录的原理是对进行distinct操作的列增加一个唯一索引。若SQL中使用了group by,则distinct是无效的,因为已经进行了分组,不会移除任何行。

9)接下来是order by,在我们得到了预期的数据后,可以对数据进行排序,以方便阅读,由于我们所执行的SQL查询出来只有一条数据,所以这里的排序效果并不明显,如下图

需要注意的是若不使用order by则查询出来的数据是无序的,并非是按照主键有序排列,这是因为关系型数据库是基于数学来实现的,关系对应数学中的集合,表中的数据对应集合中的元素,而集合本身是无序的,因此在不使用order by的情况下结果并不一定按照主键顺序进行排列,故我们在需要有序输出行记录时必须使用order by。

还有在对列进行排序时,若列没有索引,则排序会造成一定的性能开销。一般通过show status like ‘%sort%’ \G; 来观察相关变量,以此来判断是否可以通过添加索引来降低开销。

还需说明的是null值在升序中会首先被选出,因为在order by子句中null值被视为最小值。

10)最后是limit,拿到从指定位置开始的指定行数据,limit常和order by一起使用,其使用方式是limit n,m,表示拿到从第n行开始(不包含n行,实际从n+1行开始,如要取到的数据包含第n行,则应该是n-1)的共m行数据。

需要注意的是在大数据量下使用limit来分页效率是比较低的,因为需要在这么多数据量下去定位位置(定位n)。

这里详细对多表联查的过程做下说明,假设我们联查表A、B、C、D

先是表A、B做笛卡尔积,即两表记录数相乘,然后通过on来筛掉不符合的记录,如果是left join或right join这种方式的联查,则还需要保留外部行,怎么理解保留外部行,就是保留表中被过滤条件过滤掉的数据,这样就得到了表A、B的联查数据,接下来再将得到的数据和表C做笛卡尔积,并重复这一过程,直到最后一张表完成这一过程,最后就得到了A、B、C、D四表联查的数据;

额外知识点

在where子句后书写过滤条件时,有两种过滤情况是不允许发生的。

①数据没有分组前或者说在group by没有执行前,在where子句中不能使用分组函数,例如max()、min()、count()、sum()等,正因为这个限制的存在就能理解having子句存在的意义了,因为我们还存在对分组后的数据进行统计的需要;

错误的SQL:

select id from t_commodity c where count(commodity_type_id)>=5;

MySQL提示:

②为select子句后的列取别名,并在where子句里直接使用列别名,是不被允许的。因为where的执行顺序是要高于select的,因为在列还没有被选取的情况下,就开始使用列别名明显是不行的。

错误的SQL:

select `name` as n from t_commodity where n='精进';

MySQL提示:

MySQL学习分享-->查询-->查询的原理的更多相关文章

  1. MySQL学习分享--Thread pool实现

    基于<MySQL学习分享--Thread pool>对Thread pool架构设计的详细了解,本文主要对Thread pool的实现进行分析,并根据Mariadb和Percona提供的开 ...

  2. MySQL学习分享-->查询-->查询的分类

    MySQL的查询可以分为交叉联接.内联接.外联接.自然联接.straight_join 下面对于查询的学习,会用到以下四张表: create table t_commodity_type( `id` ...

  3. MySql学习(三) —— 子查询(where、from、exists) 及 连接查询(left join、right join、inner join、union join)

    注:该MySql系列博客仅为个人学习笔记. 同样的,使用goods表来练习子查询,表结构如下: 所有数据(cat_id与category.cat_id关联): 类别表: mingoods(连接查询时作 ...

  4. 【SQL】MySQL学习笔记1-----子查询

    1.什么叫子查询? 通俗的讲就是查询中有查询,SQL语句中有多个select语句. 2.什么地方可以嵌入子查询? SELECT 列 (不在标准之内) FROM 表 (可以嵌入,作为表存在) WHERE ...

  5. Entity Framework with MySQL 学习笔记一(查询)

    参考 : http://msdn.microsoft.com/en-us/data/jj574232.aspx EF 查询基本上有3中 默认是 Lazy Loading 特色是只有在需要数据的时候EF ...

  6. MySQL学习笔记(二)—查询

    一.多表连接查询 新建两张表t_user.t_order.              1.内连接      返回满足条件的所有记录. (1)显式内连接      使用inner join关键字,在on ...

  7. MySQL学习笔记(3) - 查询服务器版本,当前时间,当前用户

    SELECT VERSION(); --显示当前服务器版本 SELECT NOW(); --显示当前日期时间 SELECT USER(); --显示当前用户 MySQL中语句规范: 1.关键字和函数名 ...

  8. MySQL学习(二)索引原理及其背后的数据结构

    首先区分几个概念: 聚集索引 主索引和辅助索引(即二级索引) innodb中每个表都有一个聚簇索引(clustered index ),除此之外的表上的每个非聚簇索引都是二级索引,又叫辅助索引(sec ...

  9. MySQL学习分享--数值类型

    数值类型 MySQL的数值类型包括整数类型.浮点数类型.定点数类型.位类型. 整数类型 MySQL支持的整数类型有tinyint.smallint.mediumint.int.bigint(范围从小到 ...

随机推荐

  1. Servlet进阶API

    对于每个Servlet的设置信息,web容器会为其生成一个ServletConfig作为代表对象,可以从该对象取得Servlet初始参数,以及代表整个web应用程序的ServletContext对象. ...

  2. 在Express中安装XTemplate

    上一节讲了安装Express,并且生成了一个"microblog"的工程,我们的目标是在工程下安装XTemplate: 1.安装xtpl npm install xtpl xtem ...

  3. Bootstrap 输入组

    Bootstrap 输入组: <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  4. oracle与sqlserver利用函数生成年月日加流水号

    最近在做oracle相关的项目,刚接触oracle,与sqlserver语法上还是有区别的 sqlserver : 示例:FX+当前年月日+00001 如下图流水号实力所示 原理: 首先 'FX'是固 ...

  5. JavaScript基础:创建对象

    先来看两种简单的对象创建方式: 1.Object构造函数方法 var person = new Object(); person.name = "Nicholas"; person ...

  6. 微信小程序开发系列(二)小程序的全局文件

    其实你已经知道了小程序的文件结构 上一节讲到,小程序的页面由三部分组成: 视图(.wxml).逻辑(.js).样式(.wxss). 我们这次重新展开文件结构: 小程序用到的文件类型只有四种,正如你所看 ...

  7. HDU5726(RMQ&&二分)

    GCD Time Limit:5000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status D ...

  8. Java连接数据库(mysql,sqlserver)

    犹记当年为了使用java程序连接mysql数据库花费一天时间,最后发现是没有导入外包,如今看来真的发现自己那时有点二,也怪我使用的教科书上没有说明这点(强行甩锅,哈哈).今天分享出来,,希望后者不因为 ...

  9. css之描点定位方式

    <!-- 描点定位的两张方式 --> <!-- 1.通过id定位 --> <!-- 2.通过name定位 只能用a--> <div> <a hre ...

  10. 框架篇:Spring+SpringMVC+hibernate整合开发

    前言: 最近闲的蛋疼,搭个框架写成博客记录下来,拉通一下之前所学知识,顺带装一下逼. 话不多说,我们直接步入正题. 准备工作: 1/ IntelliJIDEA的安装配置:jdk/tomcat等..(本 ...