Mysql里UNION ALL要注意的地方

前言

比如我有一个表A:

1
2
3
4
5
6
+------+--------+-------+
| name | code | num |
+------+--------+-------+
| A | no1 | 300 |
| A | no2 | 100 |
+------+--------+-------+

我还有一个表B:

1
2
3
4
5
6
7
+------+--------+-------+
| name | code | num |
+------+--------+-------+
| A | no1 | -100 |
| A | no5 | 77 |
| B | no7 | 2 |
+------+--------+-------+

现在我想要两个表的num相加,不存在的列补全,得到一个表C:

1
2
3
4
5
6
7
8
+------+--------+-------+
| name | code | num |
+------+--------+-------+
| A | no1 | 200 |
| A | no2 | 100 |
| A | no5 | 77 |
| B | no7 | 2 |
+------+--------+-------+

请问怎么做?

答案是:

1
2
3
4
5
6
7
8
9
10
11
SELECT
u.name,
u.code,
SUM(u.num),
FROM
(
SELECT name, code, num FROM tableA
UNION ALL
SELECT name, code, num FROM tableB
) u
GROUP BY u.name, u.code;

因为表A,表B的列是一模一样的,所以可以通过UNION ALL来整合成一个大表,然后去重再求和达到这个目的,而不是死磕join。

实际例子

上面那个只是一个引子,这几天我在做一个订正功能,设计思路是这样的:原来有一个各部门的当前空余容器额度表A,这个表每日有crontab定时任务与集团的容器额度转移表同步。但是集团的表有一些实际的缺陷,所以我们还要有订正能力,于是我就按照A表的结构造了一模一样的订正表B,这样A表与B表的差不就是正确的数值了么?

但是这里面有一个很坑的事情,那就是作为标准的集团的总额度表,一次容器额度的转移是要产生三条记录,分别是cpu,memory和disk,如图:

那么我对集团的这个额度转移原表要进行二次加工,由行转列,把cpu、memory、disk按字段的方式展示出来,那么SQL语句的想法就是:先获取到每个部门的收取CPU额度表、MEMORY额度表、DISK额度表减掉他们转出的各自额度,再以部门为基准left join做出来一个大表,这样就有cpu列、memory列和disk列了。

为了防止有重复的,这里各部门的CPU表、memory表、disk表我都分别做了group by,先group byleft join,效果是OK的,就是SQL语句比较复杂,好几十行。

但是现在引入了这个订正表,而这个订正表也是“一条变更三条记录”的模式,那么我天真的认为就是cpu收入表 left join 减掉cpu转出表 UNION ALL 订正cpu表 group by 去重列 left join memory收入表 left join 减掉memory转出表 UNION ALL 订正memory表 group by 去重列 on 条件 left join disk收入表 left join 减掉disk转出表 UNION ALL 订正disk表 group by 去重列 on 条件就是答案。

但是如果这么写的话,你会发现,CPU减的是对的,MEMORY减的多了一倍,DISK减的多了两倍…

这就是UNION ALL的问题,它在CPU表去拼接MEMORY表的时候,会多出一倍,也就是笛卡尔积的时候,这个临时表里有一遍CPU的记录二遍memory的记录,所以memory就会多出来一倍的值,同理,disk就会多出来两倍的值。订正的结果自然不正确。

正确的做法就是上面那个引子那样:cpu收入表 union all 减掉cpu转出表 UNION ALL 订正cpu表 group by 去重列 left join memory收入表 union all 减掉memory转出表 UNION ALL 订正memory表 group by 去重列 on 条件 left join disk收入表 union all 减掉disk转出表 UNION ALL 订正disk表 group by 去重列 on 条件

这样各自的元素表通过两个union all链接成一个大表,通过group by去重在SUM取和,就会得到很干净的底表,如此将三张表再通过LEFT JOIN链接起来就大功告成了!

Django执行复杂SQL

上面这个SQL后来写出来非常的复杂,如果用django自带的orm来写会特别的累,所以最省事的方法就直接用SQL原生句来在Django里执行,执行方法如下:

1
2
3
4
5
6
7
8
from django.db import connection, transaction

cursor = connection.cursor() # 它会直接拿到django的数据库链接信息
sql = '''
那个非常复杂的语句
'''
cursor.execute(sql)
transaction.commit() # 执行到数据库里

感谢您请我喝咖啡~O(∩_∩)O,如果要联系请直接发我邮箱chenx1242@163.com,我会回复你的
-------------本文结束感谢您的阅读-------------