MySQL左联多个表做分组查询数据重复问题解决
经常会遇到 MySQL 左联很多个表的情况,大部分情况下,只要主表外键与每个左联表都是一对一或者多对一(hasOne)的关系,那么结果记录数就不会存在问题。但是当与左联表关系是一对多的情况下,结果会出现重复记录。这种情况除了是以左联表作为目标记录,一般都会做去重处理。
通过聚合函数(比如:COUNT
、SUM
、AVG
、MIN
、MAX
等)结合 group by
分组查询可以实现去重目的。
select a.id, a.name, count(b.id) as b_n
from a
left join b on a.id = b.a_id
group by a.id
上面是联查一个表的情况,分组查询结果没有问题。但当分组联查两个表,数据会存在重复统计的情况。因为 SQL 是先联查,然后按照联查结果分组的。结果集为 a(n)b(m)c(l),以 a 表为主表,对于表 b 聚合函数结果重复了 l 次,对于表 c 聚合结果重复了 m 次:
select a.id, a.name, count(b.id) as b_n, SUM(c.money) as c_total
from a
left join b on a.id = b.a_id
left join c on a.id = c.a_id
group by a.id
遇到这种情况脑子里第一时间想到的,就是使用子查询。
使用子查询的 SQL:
select a.id, a.name,
(select count(b.id) from b where b.a_id = a.id) as b_n,
(select SUM(c.money) from c where c.a_id = a.id) as c_total
from a
别说,子查询还挺好用的。
网上还有一种 先分组查询,再将结果作为联查表联查 的方法,他的查询 SQL 类似:
select a.id, a.name, b1.b_n, c1.c_total
from a
left join (select count(b.id) as b_n, b.a_id from b group by b.a_id) as b1
on a.id = b1.a_id
left join (select SUM(c.money) as c_total, c.a_id from c group by c.a_id) as c1
on a.id = c1.a_id
from a
执行有效。但本地测试(主表 1000 记录,两联查表 10000 条记录,联查表外键有创建索引)使用子查询方法和这种先分组再联查方法,后者比前者慢了近 10 倍。前者 0.003s,后者 0.03s 左右。
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。