如何通过带GROUP BY的单条MySQL查询获取正确的最小与最大值
MySQL: 按日期分组同时获取每日最低和最高温度的正确方法
我来帮你拆解这个问题——你想按日期分组,一次性拿到每天的最低和最高温度,但原查询结果不对,核心问题出在GROUP BY的使用逻辑和非聚合列的处理上。
先看你的问题场景
你有一份包含每日温度记录的表,每行的temp_min和temp_max是同一个值(应该是该时间点的温度),现在需要按日期汇总,得到每天的最低和最高温,同时可能想拿到日期对应的星期几,甚至温度对应的其他字段(比如气压、天气状况)。
你的原查询为什么错了?
先看你写的语句:
SELECT MIN(`temp_min`) AS `temp_min`, MAX(`temp_max`) AS `temp_max`, `dt_txt`, DAYNAME(`dt_txt`) AS `dayname`, `pressure`, `condition` FROM infoboard.forecasts WHERE `dt_txt` >= CURDATE() GROUP BY `dt_txt` ORDER BY `dt_txt` ASC;
这里有两个关键问题:
- 非聚合列的随机值问题:标准SQL要求GROUP BY子句必须包含所有非聚合函数的列,但MySQL默认允许不这么做(取决于
ONLY_FULL_GROUP_BY配置)。这就导致pressure和condition这两个列返回的是分组内随机某一行的值,完全不可控。 - 可能的日期过滤/分组偏差:如果
dt_txt是DATETIME类型(带时分秒),CURDATE()返回的是当前日期(比如2024-05-20),dt_txt >= CURDATE()会过滤掉当天早于当前时间的记录;如果要按自然日分组,应该用DATE(dt_txt)来提取日期部分,避免时间干扰。
你看到的错误温度值,大概率是因为上述问题导致的计算偏差,或者你的dt_txt字段类型和过滤逻辑不匹配。
正确的解决方案分两种情况
情况1:只需要每日的最低温、最高温、日期和星期几
这是最简单的场景,只需要用聚合函数配合GROUP BY分组列即可,完全符合标准SQL:
SELECT MIN(`temp_min`) AS `daily_min_temp`, -- 当天所有记录中的最低温 MAX(`temp_max`) AS `daily_max_temp`, -- 当天所有记录中的最高温 `dt_txt`, DAYNAME(`dt_txt`) AS `dayname` FROM infoboard.forecasts -- 如果dt_txt是DATETIME类型,用DATE()提取日期来过滤和分组 WHERE DATE(`dt_txt`) >= CURDATE() GROUP BY `dt_txt` ORDER BY `dt_txt` ASC;
这个查询会准确计算每个日期下的温度极值,结果完全可控。
情况2:还需要获取极值对应的其他字段(比如气压、天气状况)
如果你想知道当天最低温对应的气压是什么,或者最高温对应的天气状况,就需要找到对应极值的那一行数据。这里推荐两种方法:
方法1:子查询关联(兼容所有MySQL版本)
先通过子查询找到每个日期的极值,再关联原表拿到对应行的其他字段:
SELECT min_f.temp_min AS daily_min_temp, max_f.temp_max AS daily_max_temp, min_f.dt_txt, DAYNAME(min_f.dt_txt) AS dayname, min_f.pressure AS min_temp_pressure, -- 最低温对应的气压 min_f.condition AS min_temp_condition, -- 最低温对应的天气 max_f.pressure AS max_temp_pressure, -- 最高温对应的气压 max_f.condition AS max_temp_condition -- 最高温对应的天气 FROM -- 找到每个日期最低温的记录 (SELECT * FROM infoboard.forecasts f WHERE (DATE(f.dt_txt), f.temp_min) IN (SELECT DATE(dt_txt), MIN(temp_min) FROM infoboard.forecasts GROUP BY DATE(dt_txt))) min_f JOIN -- 找到每个日期最高温的记录 (SELECT * FROM infoboard.forecasts f WHERE (DATE(f.dt_txt), f.temp_max) IN (SELECT DATE(dt_txt), MAX(temp_max) FROM infoboard.forecasts GROUP BY DATE(dt_txt))) max_f ON DATE(min_f.dt_txt) = DATE(max_f.dt_txt) WHERE DATE(min_f.dt_txt) >= CURDATE() ORDER BY min_f.dt_txt ASC;
方法2:窗口函数(MySQL 8.0+推荐,更简洁)
窗口函数可以直接在分组内给记录排名,轻松拿到极值对应的行:
WITH ranked_forecasts AS ( SELECT *, -- 按日期分组,按最低温升序排名,第一行就是最低温记录 ROW_NUMBER() OVER (PARTITION BY DATE(dt_txt) ORDER BY temp_min ASC) AS min_rank, -- 按日期分组,按最高温降序排名,第一行就是最高温记录 ROW_NUMBER() OVER (PARTITION BY DATE(dt_txt) ORDER BY temp_max DESC) AS max_rank FROM infoboard.forecasts WHERE DATE(dt_txt) >= CURDATE() ) SELECT min_f.temp_min AS daily_min_temp, max_f.temp_max AS daily_max_temp, min_f.dt_txt, DAYNAME(min_f.dt_txt) AS dayname, min_f.pressure AS min_temp_pressure, min_f.condition AS min_temp_condition, max_f.pressure AS max_temp_pressure, max_f.condition AS max_temp_condition FROM ranked_forecasts min_f JOIN ranked_forecasts max_f ON DATE(min_f.dt_txt) = DATE(max_f.dt_txt) WHERE min_f.min_rank = 1 AND max_f.max_rank = 1 ORDER BY min_f.dt_txt ASC;
验证结果
用你给的示例数据,情况1的查询会返回:
+----------------+----------------+----------+--------+ | daily_min_temp | daily_max_temp | dt_txt | dayname | +----------------+----------------+----------+--------+ | 5.64 | 14.14 | 2018-05-14| Monday | | 8.77 | 13.86 | 2018-05-15| Tuesday | +----------------+----------------+----------+--------+
内容的提问来源于stack exchange,提问作者6bytes
相关产品推荐
相关产品推荐

