SQL Select 语句数据库执行原理小记
子句执行顺序:
一个select语句由多条子句组成,数据库执行select查询时是按照一定的顺序执行各条子句的,执行一条子句会生成一个临时结果集,这个临时结果集又作为下一条子句执行的数据基础。
select语句从“from...where”子句开始,向下依次执行各条子句,最后执行select子句,完成查询结果的显示。
分组查询与非分组查询:
select语句首先执行的“from...where”子句,from后面跟着的是原始数据(可能是通过“join...on”等数据库提供的方法把多个数据表的数据结合到一起形成的一个庞大的数据块),where后面跟着的,是对原始数据进行筛选的条件表达式,数据库用这个条件表达式对原始数据一条条进行筛选计算,丢弃条件表达式计算结果为“假”的数据,筛选出表达式计算结果为“真”的数据,形成我们的第一级临时结果集。
假如没有“group by...having”子句,select语句执行完“from...where”子句以后,生成第一级临时结果集,就开始在第一级临时结果集的基础上进行最终的数据显示,按照select关键字后面列出的字段依次显示第一级临时结果集中的数据,也就是我们查询最终形成的结果,这个查询就是非分组查询。
数据库执行完“from...where”子句,形成第一级临时结果集,如果存在“group by...having”子句,就切换到分组查询模式开始执行“group by...having”子句。数据库一旦切换到了分组查询模式,之后所有的运算和最终的显示都是基于分组的,select查询所面对的就不再是一条条的数据,而是一个个的分组,select语句显示的结果与分组对应,有几个分组查询就显示几行结果。
数据库根据“group by”关键字后面列出的字段的值的不同对第一级临时结果集中的数据进行分组,然后根据having关键字后面的条件表达式对分组进行筛选,筛选出来符合条件的分组数据,组合形成第二级临时结果集。
如果select语句中没有“group by”子句,那select查询就会按非分组查询往下走,如果select关键字后面用到了聚合函数,数据库将直接转换为分组查询模式,它把所有的已有数据直接作为一个分组看待,从而应用聚合函数进行计算;因为所有数据都被作为一个分组对待select查询显示的结果只会有一行数据。
字段有效性问题:
存在“group by...having”子句的分组查询,分组中所有数据在分组依据的字段上具有相同值,故分组依据的字段是有效字段,其它字段全部变成非有效字段。此种情况下,having后面的条件表达式可以应用有效字段和聚合函数(聚合函数参数可以应用所有字段);最终的显示结果只能由“group by”分组所依据的有效字段和聚合函数计算结果组成。
无“group by...having”子句,由于select关键字后面用到聚合函数而造成的分组查询,所有原始字段全部为无效字段,最终的显示结果只能由聚合函数计算结果组成。
聚合函数是以分组为基础,针对分组进行计算的,只要分组形成后面的运算即可以应用聚合函数。聚合函数是在分组的基础上对分组中的数据进行统计计算,而组成分组的数据在各个字段上都是有确定的值的,所以聚合函数的统计计算是有数据可依的,是可以以各个字段作为参数的。
聚合函数(SUM、MAX,MIN,AVG,COUNT),除 COUNT (*)以外,聚合函数忽略空值。
补充更正:
在分组查询模式下,Having 子句和Select子句中可以直接使用的字段为:
1、Group By 分组所依据的字段。
2、聚合函数运算结果。
3、与分组不存在依赖关系的数据显示(如:“create table sc(sno int, cno int)”“ select sno, 5+6 as x from sc group by sno”,这个x值返回5+6的计算结果11,与分组无关,语句可以执行)。
4、Group By 分组所依据的字段是数据表的主键或是具有唯一性约束的字段,这时候数据库猜测其它字段与该分组字段直接相关也具有唯一性,允许你使用其它字段。这时候,数据的准确性、可靠性就必须由数据库使用者自己来作出保证了!!!比如一个包含人员信息的表中,其中包括两个字段:UserID、UserName(这时候就先别说数据库范式规则了),UserID是主键,以UserID分组,也就是以一个人的数据为一个分组,那么这个分组内所有数据的UserName肯定是相同的,这个时候可以考虑在以UserID分组情况下同时查询显示UserName值。作为程序员,必须有意识地保持自己对数据准确性近乎苛刻的严谨态度,强烈建议慎用这种方式!!!
MySQL在5.7版本之前,对此要求不严格,基本上可以和SQLite一样直接查询这些无效字段。在5.7版本之后,语法开始严格化,对于以上四种情况之外,无效字段不允许查询。但为了兼容,引入了ONLY_FULL_GROUP_BY 模式开关,并默认开启该模式开关,在ONLY_FULL_GROUP_BY下对查询的字段严格限制。如果用户需要解除MySQL对查询字段的限制,可以强制关闭ONLY_FULL_GROUP_BY 模式。
where 、having两个条件表达式的区别:
where条件表达式,作用于分组之前,是对原始数据的直接筛选,计算结果为“假”的数据将直接被丢弃掉,不会成为后面分组中的数据。因为此时还没有分组,聚合函数是基于分组进行计算的,所以在where后面的这个条件表达式中不能应用聚合函数!
having条件表达式,作用于“group by”分组之后,此时分组已经形成,所以聚合函数在此都可以应用。原始各字段,可以应用有效字段(分组依据的字段),不能应用无效字段;聚合函数参数可以应用所有原始字段。
“group by”多级分组:
group by关键字后面跟的是字段名列表,分组可以依据多个字段进行。有些时候,我们对数据分组,可能还需要在一次分组的基础之上再进行二次分组。
比如,学校一个年级的年级组长,有年级组所有学生的数据,现在需要分班统计每个班里的男生、女生数量。首先以班级字段分组,每个班形成一个大组;然后在这个基础上,再以性别字段进行二次分组,每个班再生成男生组、女生组二个小组;最后再把所有小组分别计数统计,就达到了按班分别统计男生、女生数量的目的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通