在处理一个sql语句要求选择分组中最新的一条数据时,发现网上有一些mysql语句查询字段不在group by中,sql依然可以运行,在我本地运行后日志报错:
Caused by: org.b3log.latke.repository.RepositoryException: java.sql.SQLSyntaxErrorException: Expression #20 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'solo.aa.oId' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
sql_mode=ONLY_FULL_GROUP_BY问题出现在mysql5.7之后版本,默认的sql_mode为:
ONLY_FULL_GROUP_BY, STRICT_TRANS_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER, NO_ENGINE_SUBSTITUTION
select @@session.sql_mode select @@golbal.sql_mode # 设置sql_mode,语法相同全局替换即可 set session sql_molde='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; set @session.sql_molde='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
sql_molde='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
SELECT * FROM aaaa GROUP BY student_id;
结果(只会选一条语句):
增加过滤条件>50,可以看到where子句在group by排序之前执行,having在分组后执行,其结果如下所示:
SELECT t.*, MAX(t.line) lineMax FROM (SELECT * FROM aaaa ORDER BY line DESC ) t GROUP BY student_id;
结果中返回line值与最大值不一致,也就是值取到了最大值,但是并没有将最大值那条记录查询出来。
修改括号中的子句增加limit
SELECT t.*, MAX(t.line) lineMax FROM (SELECT * FROM aaaa ORDER BY line DESC LIMIT 100 ) t GROUP BY student_id;
此次返回结果,通过查看id以及line值可以看到获取到了我们想要最大值记录。
注意:
limit 是必须要加的,如果不加的话,数据不会先进行排序,通过 explain 查看执行计划,可以看到没有 limit 的时候,少了一个 DERIVED 操作。
在日常工作中,我们会有时会开慢查询去记录一些执行时间比较久的SQL语句,找出这些SQL语句并不意味着完事了,些时我们常常用到explain这个命令来查看一个这些SQL语句的执行计划,查看该SQL语句有没有使用上了索引,有没有做全表扫描,这都可以通过explain命令来查看。所以我们深入了解MySQL的基于开销的优化器,还可以获得很多可能被优化器考虑到的访问策略的细节,以及当运行SQL语句时哪种策略预计会被优化器采用。
-- 对比一下以下语句的差异 EXPLAIN select * from aaaa ; EXPLAIN select * from aaaa where id = '1'; EXPLAIN select * from aaaa where type = '1';
以上查询结果中各个表头字段的意义请查阅此超链接
我们在这里通过EXPLAIN 查看一下上述sql语句,发现增加limit后多了一个记录。