You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MySQL中GROUP BY取MAX后限制每组1条结果及分组报错问题

解决同一Task对应多条最大end_at的TaskPlan时仅返回一条的问题

我来帮你搞定这个问题——你需要的是每个Task只返回一条对应最大end_at的TaskPlan,哪怕同一任务下有多个计划共享这个最大结束时间对吧?咱们先理清楚问题根源,再给出靠谱的解决方案。

问题根源分析

你的原始查询之所以会返回多条结果,是因为子查询会把所有满足对应task_id最大end_at的TaskPlan都捞出来,只要有多个计划在同一时间结束,就会出现重复的task_id记录。

而你尝试的GROUP BY写法报错,是因为MySQL的only_full_group_by严格模式要求:SELECT列表里的非聚合列必须要么出现在GROUP BY子句中,要么是被GROUP BY列函数依赖的(比如主键)。显然TaskPlan.id既不在GROUP BY里,也不满足函数依赖,所以直接报错。

可行解决方案

下面提供几种实用的实现方式,按推荐程度排序:

方法1:窗口函数(最推荐)

如果你的MySQL版本是8.0及以上(支持窗口函数),这是最清晰高效的方案。用ROW_NUMBER()给每个task_id分组内的记录排序,取每组的第一条即可:

SELECT t.*, pt.*
FROM Task AS t
INNER JOIN (
    SELECT 
        *,
        -- 按task_id分组,先按end_at降序(最大时间在前),再按id升序(相同时间取id最小的)
        ROW_NUMBER() OVER (PARTITION BY task_id ORDER BY end_at DESC, id ASC) AS row_num
    FROM TaskPlan
) AS pt ON pt.task_id = t.id
WHERE t.status = 'plan' AND pt.row_num = 1;
  • 你可以根据需求调整ORDER BY的第二条件:比如改成id DESC就会取相同end_at里id最大的那条,或者其他字段排序。
  • 这种方式性能很好,只要给TaskPlan表的task_id和end_at加个联合索引,查询速度会非常快。

方法2:关联子查询(兼容旧版本MySQL)

如果你的MySQL版本低于8.0(不支持窗口函数),可以用这种嵌套子查询的方式,先找到每个任务的最大end_at,再在对应记录里挑一条:

SELECT t.*, pt.*
FROM Task AS t
INNER JOIN TaskPlan AS pt ON pt.task_id = t.id
WHERE t.status = 'plan'
-- 第一步:匹配当前任务的最大end_at
AND (pt.task_id, pt.end_at) = (
    SELECT task_id, MAX(end_at)
    FROM TaskPlan
    WHERE task_id = t.id
    GROUP BY task_id
)
-- 第二步:在相同task_id和end_at的记录里,取id最小的那条(可改ORDER BY换规则)
AND pt.id = (
    SELECT id
    FROM TaskPlan
    WHERE task_id = t.id AND end_at = pt.end_at
    ORDER BY id ASC
    LIMIT 1
);

方法3:聚合拼接(不推荐,仅作兼容参考)

这种方式是把end_at和id拼接成字符串后取最大值,再拆分出id,可读性较差,但能兼容非常旧的MySQL版本:

SELECT t.*, pt.*
FROM Task AS t
INNER JOIN (
    SELECT 
        task_id,
        -- 拆分出最大拼接字符串对应的id
        SUBSTRING_INDEX(MAX(CONCAT(end_at, '_', id)), '_', -1) AS plan_id,
        MAX(end_at) AS max_end_at
    FROM TaskPlan
    GROUP BY task_id
) AS agg ON agg.task_id = t.id
INNER JOIN TaskPlan AS pt ON pt.id = agg.plan_id
WHERE t.status = 'plan';
  • 原理是利用timestamp的字符串排序特性,相同end_at下,id更大的拼接字符串会更大,所以MAX()会拿到对应id;如果要取id最小的,可以把拼接改成CONCAT(end_at, '_', LPAD(id, 10, '0'))再取MIN()。

总结

优先用窗口函数的方案,代码清晰、性能最优。如果必须兼容旧版本MySQL,再考虑关联子查询的方式。

内容的提问来源于stack exchange,提问作者toing_toing

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:06:18