使用GROUP BY后MySQL UNION查询仍返回重复值问题排查
解决新闻列表重复问题 & 原查询GROUP BY无效的原因
首先,咱们先搞定重复的问题,再聊聊为什么你的GROUP BY没起作用。
一、正确的查询方案(避免重复)
你的核心需求是:获取所有由17152创建的新闻(不管是英文还是西班牙文),且同一新闻只返回一次。最直接的方式是从主表z_news_master出发,判断它是否关联到任意一个语言表中符合条件的记录,而不是从语言表反向关联主表再合并。
方案1:用EXISTS精准筛选(推荐)
这个方案逻辑清晰,性能也不错,直接从主表取唯一记录:
SELECT m.id, m.title, m.created_by, m.publish_up, m.state FROM z_news_master m WHERE -- 存在英文关联记录且创建者是17152 EXISTS ( SELECT 1 FROM z_news_english e WHERE e.reference_id = m.id AND e.created_by = 17152 ) -- 或者存在西班牙文关联记录且创建者是17152 OR EXISTS ( SELECT 1 FROM z_news_spanish s WHERE s.reference_id = m.id AND s.created_by = 17152 ) -- 按需排序,比如按发布时间倒序 ORDER BY m.publish_up DESC;
方案2:如果需要语言表的额外字段
如果你还想获取语言表的某些信息(比如区分新闻是英文还是西班牙文),可以用LEFT JOIN加DISTINCT确保主表记录唯一:
SELECT DISTINCT m.id, m.title, m.publish_up, m.state, -- 取任意一个存在的创建者(这里都是17152,所以结果一致) COALESCE(e.created_by, s.created_by) AS created_by, -- 可选:标记新闻的语言类型 CASE WHEN e.id IS NOT NULL THEN 'English' WHEN s.id IS NOT NULL THEN 'Spanish' ELSE 'Unknown' END AS language FROM z_news_master m LEFT JOIN z_news_english e ON m.id = e.reference_id AND e.created_by = 17152 LEFT JOIN z_news_spanish s ON m.id = s.reference_id AND s.created_by = 17152 -- 确保至少关联一个语言表的记录 WHERE e.id IS NOT NULL OR s.id IS NOT NULL ORDER BY m.publish_up DESC;
二、为什么你的原查询GROUP BY没解决重复?
咱们拆解一下你的原查询逻辑和问题根源:
- UNION的去重规则:
UNION本身会自动去重,但前提是两条记录的所有列值完全一致。如果你的两个子查询返回的记录,虽然id相同,但其他列(比如title、publish_up)来自不同的表且值不同(比如英文标题和西班牙文标题不一样),UNION会认为这是两条不同的记录,不会合并。 - GROUP BY的作用限制:你在
UNION之后加GROUP BY id,理论上会把同一id的记录合并成一行,但这里有两个问题:- 如果你的SQL模式开启了
ONLY_FULL_GROUP_BY(MySQL默认开启),这个查询会直接报错,因为title、created_by等列不在GROUP BY中,也没有聚合函数(比如MAX(title))。 - 如果没开启
ONLY_FULL_GROUP_BY,数据库会随机选择分组内某一行的列值返回,但你可能会误以为还是重复(其实是同一id的一条记录,但内容是其中一个语言的),而且这种行为是不可控的。
- 如果你的SQL模式开启了
- 子查询的ORDER BY无效:另外,你在子查询里加的
ORDER BY c.id DESC其实是无效的——在UNION的子查询中,ORDER BY如果没有搭配LIMIT,数据库会忽略这个排序,所以你的子查询排序不会生效。
简单说,你的原查询思路是从语言表“拉”主表数据,同一主表会被两个语言表各拉一次,导致生成两条记录;而如果这两条记录的列值有差异,UNION和GROUP BY都无法按你预期的方式合并它们。
内容的提问来源于stack exchange,提问作者Saymon
相关产品推荐
相关产品推荐

