MySQL中如何结合JOIN与HAVING?GROUP BY后关联表去重求助
嘿,我来帮你捋捋这个问题!你遇到的重复问题,根源在于关联操作和GROUP BY的顺序搞反了。
原本单独对tbl_warning按staff_id分组时,每个staff_id只会返回一条记录(前提是你的MySQL没有开启ONLY_FULL_GROUP_BY,这里先不展开这个规则),但当你先关联tbl_employment和tbl_staff再分组时,关联过程会先把所有匹配的行拉出来——如果某个staff_id在关联表中有多条对应记录(比如员工有多个雇佣记录),就会生成多条临时行,之后再分组就可能出现重复的结果,甚至破坏你原本想要的“每个staff_id唯一”的逻辑。
下面给你几个针对性的解决方案:
方案1:先分组统计,再关联其他表
这是最直接的解决思路:先把tbl_warning按staff_id分组的结果作为子查询,再和其他表做关联。这样子查询里每个staff_id只有一条记录,关联后自然不会出现重复。
示例SQL:
SELECT w.id, w.staff_id, w.note, w.warning_date, e.com_id AS comid, s.name, s.gender FROM ( -- 先完成分组逻辑,得到每个staff_id对应的警告记录 SELECT id, staff_id, note, warning_date FROM tbl_warning GROUP BY staff_id HAVING COUNT(staff_id) > 0 ) AS w -- 关联雇佣表和员工表 JOIN tbl_employment e ON w.staff_id = e.staff_id JOIN tbl_staff s ON w.staff_id = s.staff_id;
⚠️ 注意:如果tbl_warning里同一个staff_id有多条警告记录,上面的子查询会随机选一条(MySQL在未开启ONLY_FULL_GROUP_BY时的默认行为)。如果你需要特定的记录(比如最新的警告),可以调整子查询,比如:
SELECT id, staff_id, note, warning_date FROM tbl_warning w WHERE warning_date = ( SELECT MAX(warning_date) FROM tbl_warning WHERE staff_id = w.staff_id );
方案2:用窗口函数筛选唯一staff_id的指定记录
如果你需要保留每个staff_id的特定警告记录(比如最新的),同时关联其他表,窗口函数ROW_NUMBER()会是更灵活的选择:
SELECT id, staff_id, note, warning_date, comid, name, gender FROM ( SELECT w.id, w.staff_id, w.note, w.warning_date, e.com_id AS comid, s.name, s.gender, -- 按staff_id分组,给每条记录按警告日期倒序编号 ROW_NUMBER() OVER (PARTITION BY w.staff_id ORDER BY w.warning_date DESC) AS rn FROM tbl_warning w JOIN tbl_employment e ON w.staff_id = e.staff_id JOIN tbl_staff s ON w.staff_id = s.staff_id ) AS t -- 只取每个staff_id的第一条(最新的)记录 WHERE rn = 1;
方案3:用DISTINCT去重(适合保留所有警告记录但去重关联结果)
如果你的需求是显示所有警告记录,但要去掉因为关联表导致的重复行,可以用DISTINCT关键字:
SELECT DISTINCT w.id, w.staff_id, w.note, w.warning_date, e.com_id AS comid, s.name, s.gender FROM tbl_warning w JOIN tbl_employment e ON w.staff_id = e.staff_id JOIN tbl_staff s ON w.staff_id = s.staff_id;
这个方法适合当关联表中同一个staff_id有重复记录,但你只需要保留唯一的字段组合时使用。
另外还要提醒你:如果tbl_employment或tbl_staff中一个staff_id对应多条有效记录(比如员工换过公司,有多个com_id),那你得先确定要取哪个值,比如最新的雇佣记录,这时候可能需要先对关联表也做分组处理哦。
内容的提问来源于stack exchange,提问作者Sen Soeurn

