MySQL join 3 table & aggregate each without subquery

klsxnrf1  于 2023-05-21  发布在  Mysql
关注(0)|答案(2)|浏览(112)

有3个表,ABC
A有一个id列,BC整数列表示钱,a_id引用表A中的id。
以下是表A、B、C的示例
表A
| 身份证|
| --------------|
| 1|
| 2|
| 3|
表B
| 身份证|a_id|钱|
| --------------|--------------|--------------|
| 1| 1|一千|
| 2| 1|二千年|
| 3| 2|三千五|
表C
| 身份证|a_id|钱|
| --------------|--------------|--------------|
| 1| 1|一百|
| 2| 2|两百|
| 3| 2|四百|
在这种情况下,我想把它们聚合成这样。
| a_id| B_money_sum| C_money_sum|
| --------------|--------------|--------------|
| 1|二千年|一百|
| 2|三千五|六百|

| a_id|货币总额|
| --------------|--------------|
| 1|两千一百|
| 2| 4100|
我试过了,但结果不是预期的。(聚合基于笛卡尔积)

select a_id, sum(b.money) B_money_sum, sum(c.money) C_money_sum
from A
join B on A.id = B.a_id
join C on A.id = C.a_id
group by a_id

是不是可以只连接,不子查询?

cu6pst1q

cu6pst1q1#

是不是可以只连接,不子查询?
否,因为当每个id(TableA)有多个a_id(TableB和TableC)时,会进行一对多连接,这会创建更多的行(本质上是行的复制,也称为“关系乘法”)。
解决该问题的一种常用方法是将SUM()隔离在单独的查询(子查询)中

select a.id, B_money_sum , C_money_sum
from tableA a 
join (select a_id,sum(money) as B_money_sum
      from tableB
      group by a_id 
     ) b on a.id = b.a_id
join (select a_id,sum(money) as C_money_sum
      from tableC
      group by a_id 
     ) c on a.id = c.a_id  ;

https://dbfiddle.uk/coZvQsuT

xzlaal3s

xzlaal3s2#

如果表b有一个id而c没有,这就有点棘手了,反之亦然。让我们以这个案例为例。

create table a (id int);
create table b (id int,a_id int,money int);
create table c (id int,a_id int,money int);
insert a values(1),(2),(3),(4);
insert b values(1,1,1000),(2,1,2000),(3,2,3500),(4,4,1000),(5,4,7000);
insert c values(1,1,100),(2,2,200),(3,2,400),(4,3,200),(5,3,600);

在这种情况下,B具有id 4,而c具有id 3。如果我们使用内部连接,id 3和id 4的数据在结果集中都会丢失:

select a_id,sum_a,sum_b 
from
    (select a_id ,sum(money) sum_a
    from a
    join b on a.id=b.a_id
    group by a_id) tb
join
    (select a_id ,sum(money) sum_b
    from a
    join c on a.id=c.a_id
    group by a_id) tc
using(a_id)
;

+------+-------+-------+
| a_id | sum_a | sum_b |
+------+-------+-------+
|    1 |  3000 |   100 |
|    2 |  3500 |   600 |
+------+-------+-------+

但是,如果我们使用单侧外连接,我们仍然会在另一侧丢失一些数据。

select a_id,sum_a,sum_b 
from
    (select a_id ,sum(money) sum_a
    from a
    join b on a.id=b.a_id
    group by a_id) tb
left join
    (select a_id ,sum(money) sum_b
    from a
    join c on a.id=c.a_id
    group by a_id) tc
using(a_id)
;

+------+-------+-------+
| a_id | sum_a | sum_b |
+------+-------+-------+
|    1 |  3000 |   100 |
|    2 |  3500 |   600 |
|    4 |  8000 |  NULL |
+------+-------+-------+

select a_id,sum_a,sum_b 
from
    (select a_id ,sum(money) sum_a
    from a
    join b on a.id=b.a_id
    group by a_id) tb
right join
    (select a_id ,sum(money) sum_b
    from a
    join c on a.id=c.a_id
    group by a_id) tc
using(a_id)
;

+------+-------+-------+
| a_id | sum_a | sum_b |
+------+-------+-------+
|    1 |  3000 |   100 |
|    2 |  3500 |   600 |
|    3 |  NULL |   800 |
+------+-------+-------+

为了弥合所有边差距,需要完全连接。但是由于MySQL还没有实现完全连接,所以需要调用UNION来合并两个外部连接。

select * from
(select a_id,sum_a,sum_b 
from
    (select a_id ,sum(money) sum_a
    from a
    join b on a.id=b.a_id
    group by a_id) tb
left join
    (select a_id ,sum(money) sum_b
    from a
    join c on a.id=c.a_id
    group by a_id) tc
using(a_id)

UNION

select a_id,sum_a,sum_b 
from
    (select a_id ,sum(money) sum_a
    from a
    join b on a.id=b.a_id
    group by a_id) tb
right join
    (select a_id ,sum(money) sum_b
    from a
    join c on a.id=c.a_id
    group by a_id) tc
using(a_id)
) t_a_b
order by a_id
;

+------+-------+-------+
| a_id | sum_a | sum_b |
+------+-------+-------+
|    1 |  3000 |   100 |
|    2 |  3500 |   600 |
|    3 |  NULL |   800 |
|    4 |  8000 |  NULL |
+------+-------+-------+

注意:最外层需要使用ORDER BY子句,这在UNION中无效。

相关问题