关于order by,group by 你需要注意这些

一、需求

我们先来说说,实际开发中遇到的需求。多个学校,一个学校多个班级。学校学生上学、放学经过校门,智能手环会自动打卡,上传当前学生体温。教育局需要查看某校某班当天的体温详情,若当天该学生体温有出现异常情况,则显示异常的体温,若该学生体温正常,则显示最新的体温数据。

二、数据库

为了方便大家理解,在这里简化了数据库。数据库字段如下:
在这里插入图片描述

CREATE TABLE `oa_student_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `corp_id` varchar(24) DEFAULT NULL,
  `department_id` int(11) DEFAULT NULL COMMENT '班级ID',
  `student_id` bigint(20) DEFAULT NULL COMMENT '学生id',
  `student_name` varchar(64) DEFAULT NULL COMMENT '学生姓名',
  `rule_name` varchar(100) DEFAULT NULL COMMENT '规则名称',
  `rule_item_id` bigint(20) DEFAULT NULL COMMENT '规则项id',
  `is_abnormal` int(2) DEFAULT NULL COMMENT '是否异常0-正常1-异常',
  `temperature` decimal(11,2) DEFAULT NULL COMMENT '温度',
  `date` date DEFAULT NULL COMMENT '打卡日期',
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_student` (`student_id`) USING BTREE,
  KEY `idx_date` (`date`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COMMENT='学生考勤记录表';

INSERT INTO `oa_student_log`(`id`, `corp_id`, `department_id`, `student_id`, `student_name`, `rule_name`, `rule_item_id`, `is_abnormal`, `temperature`, `date`, `create_time`, `update_time`) VALUES (1, '1', 34, 1, 'zzb', '上午进校考勤', 11, 0, 36.00, '2020-07-21', '2020-07-21 09:00:46', '2020-07-21 09:00:50');
INSERT INTO `oa_student_log`(`id`, `corp_id`, `department_id`, `student_id`, `student_name`, `rule_name`, `rule_item_id`, `is_abnormal`, `temperature`, `date`, `create_time`, `update_time`) VALUES (2, '1', 34, 1, 'zzb', '上午离校考勤', 12, 0, 36.50, '2020-07-21', '2020-07-20 11:59:46', '2020-07-21 11:59:50');
INSERT INTO `oa_student_log`(`id`, `corp_id`, `department_id`, `student_id`, `student_name`, `rule_name`, `rule_item_id`, `is_abnormal`, `temperature`, `date`, `create_time`, `update_time`) VALUES (3, '1', 34, 1, 'zzb', '下午进校考勤', 13, 1, 37.50, '2020-07-21', '2020-07-21 13:59:46', '2020-07-21 13:59:50');
INSERT INTO `oa_student_log`(`id`, `corp_id`, `department_id`, `student_id`, `student_name`, `rule_name`, `rule_item_id`, `is_abnormal`, `temperature`, `date`, `create_time`, `update_time`) VALUES (4, '1', 34, 1, 'zzb', '下午离校考勤', 14, 0, 36.40, '2020-07-21', '2020-07-21 16:59:46', '2020-07-21 16:59:50');
INSERT INTO `oa_student_log`(`id`, `corp_id`, `department_id`, `student_id`, `student_name`, `rule_name`, `rule_item_id`, `is_abnormal`, `temperature`, `date`, `create_time`, `update_time`) VALUES (5, '1', 34, 2, 'zzc', '上午进校考勤', 11, 0, 36.50, '2020-07-21', '2020-07-21 09:00:46', '2020-07-21 09:00:50');
INSERT INTO `oa_student_log`(`id`, `corp_id`, `department_id`, `student_id`, `student_name`, `rule_name`, `rule_item_id`, `is_abnormal`, `temperature`, `date`, `create_time`, `update_time`) VALUES (6, '1', 34, 2, 'zzc', '上午离校考勤', 12, 0, 36.59, '2020-07-21', '2020-07-20 11:59:46', '2020-07-21 11:59:50');
INSERT INTO `oa_student_log`(`id`, `corp_id`, `department_id`, `student_id`, `student_name`, `rule_name`, `rule_item_id`, `is_abnormal`, `temperature`, `date`, `create_time`, `update_time`) VALUES (7, '1', 34, 2, 'zzc', '下午进校考勤', 13, 0, 36.80, '2020-07-21', '2020-07-21 13:59:46', '2020-07-21 13:59:50');
INSERT INTO `oa_student_log`(`id`, `corp_id`, `department_id`, `student_id`, `student_name`, `rule_name`, `rule_item_id`, `is_abnormal`, `temperature`, `date`, `create_time`, `update_time`) VALUES (8, '1', 34, 2, 'zzc', '下午离校考勤', 14, 0, 36.40, '2020-07-21', '2020-07-21 16:59:46', '2020-07-21 16:59:50');

筛选后表中数据如下:
在这里插入图片描述
实现目标:
同时取出zzb中的发热的数据与取出zzc中的最新数据即下午离校考勤的数据。

三、sql语句及踩坑

SELECT
	* 
FROM
	oa_student_log o 
WHERE
	o.corp_id = 1 AND o.department_id = 34 AND o.date = '2020-07-21'
GROUP BY
	o.student_id
ORDER BY
    o.is_abnormal DESC,o.update_time DESC 

运行结果:
在这里插入图片描述
发现报错了,大概意思就是说启用了only_full_group_by SQL模式,那么MySQL就会拒绝选择列表、条件或顺序列表引用的查询,这些查询将引用组中未命名的非聚合列,而不是在功能上依赖于它们。也就是说,使用了group by,那么select的列都要在group中,或者是其他聚合列(SUM,MAX等等)
原因是:
MySQL 5.7.5和up实现了对功能依赖的检测。(在5.7.5之前,MySQL没有检测到功能依赖项,only_full_group_by在默认情况下是不启用的。)
找到了问题的所在,我们开始解决问题:
(1) 首先我们查看sql_mode的值,在sql中执行下列命令:

SHOW SESSION VARIABLES
SHOW GLOBAL VARIABLES

结果:我们发现线程变量跟全局变量sql_mode都是为only_full_group_by
在这里插入图片描述
(2)接下来,我们只需要去掉默认的only_full_group_by即可。在sql中执行下面语句:

set global sql_mode='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_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';

(3)再次执行,先前的代码,结果如下:
在这里插入图片描述
发现!!结果错了!再来分析一下SQL语句:
思考:Order By没有起作用,SQL语句去掉Order By之后重新执行语句,发现结果相同!
原因:SQL语句的执行顺序,是from,where,Group By,然后才执行Select,Order By.

解决:先排序,再分组。

  1. 我们很容易想到,直接把排序好的数据作为查询的表,然后再进行分组
select *
from (SELECT * 
	  FROM oa_student_log o 
	  WHERE
	  o.corp_id = 1 AND o.department_id = 34 AND o.date = '2020-07-21'
      OrDer  BY
	  o.is_abnormal DESC,o.update_time DESC ) AS a
GROUP BY a.student_id

结果:
在这里插入图片描述
我们发现:结果还是错误的!!!
原因:5.7版本的MySql在执行这条sql时缺少了一个derived操作,通过查阅相关资料了解到MySql 5.7对子查询进行了优化,认为子查询中的order by可以进行忽略,只要Derived table里不包含如下条件就可以进行优化:

1、UNION clause
2、GROUP BY
3、DISTINCT
4、Aggregation
5、LIMIT or OFFSET
  1. 通过第一种方法我们可以知道,超过5.7版本的MySql我们可以通过增加限制条件来实现,执行代码如下:
select *
from (SELECT * 
	  FROM
		oa_student_log o 
	  WHERE
		o.corp_id = 1 AND o.department_id = 34 AND o.date = '2020-07-21'
      OrDer BY o.is_abnormal DESC,o.update_time DESC limit 9999) AS a
GROUP BY a.student_id

结果:
在这里插入图片描述
结果正确!

  1. 其实还有一种方法,但是这种方法不是通用的!具体问题具体分析
select *
from (SELECT * 
	  FROM
		oa_student_log o 
	  WHERE
		o.corp_id = 1 AND o.department_id = 34 AND o.date = '2020-07-21'
	  GROUP BY
		o.is_abnormal DESC,o.student_id,o.update_time DESC) AS a
GROUP BY a.student_id

先根据体温是否异常进行降序分组,这样的话异常体温会优先在前面显示,再对学生id进行分组,这样可以将学生区分开,最后对同一个学生同体温状态的情况,进行按时间排序,输出结果为:
在这里插入图片描述
结果正确

猜你喜欢

转载自blog.csdn.net/xmt1369758466/article/details/107564536