union和union all去重SQL真实案例
以下是真实的生产业务SQL,我只是把表名更换了一下:(就是捞取两张表的两列数据再去重)
sql
SELECT DISTINCT user_id, user_name
FROM (
SELECT DISTINCT
manager_id AS user_id,
manager_name AS user_name
FROM demo_order_detail
WHERE status = 1
UNION ALL
SELECT DISTINCT
operator_id AS user_id,
operator_name AS user_name
FROM demo_task
WHERE status = 1
) t;
上面的SQL中,用了union all,那么我们用两个测试表看一下select distinct union all和直接用select union它们的执行过程树形图是什么样的:
只有select union的SQL场景

他的过程是:
sql
user
↓
recharge
↓
边合并边物化,并在这个 UNION 结果层完成去重
select distinct union all的SQL场景

他的过程是:
sql
user
↓
recharge
↓
先全部 UNION ALL
↓
形成完整的 t
↓
再扫描 t
↓
重新建临时表
↓
最后 DISTINCT 去重
数据量很小的时候,这两种SQL的差异是很难凸现出来的,那么我们试一下更大的百万级数据量。
百万计数据量时,两种性能的对比
测试表结构:
sql
-- 用户表
CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(20)
);
-- 用户充值表
CREATE TABLE recharge (
id INT PRIMARY KEY,
user_id INT,
amount DECIMAL(10,2),
KEY idx_user_id (user_id)
);
我们先看下表数据量(100万用户,300万条充值流水):

分别explain看执行计划
只有union的过程

sql
recharge 300万
↓
idx_user_id 覆盖索引扫描
↓
第一次去重
300万 → 10万
↓
UNION ALL
↓
最终 DISTINCT
↓
100万
这个执行过程生成耗时4.35s
union al加distinct的过程

sql
user 100万
\
UNION ALL
/
recharge 300万
↓
中间结果约 400万
↓
再做一次最终 DISTINCT
↓
100万
生成执行计划耗时9.44秒,明显比直接union的慢了不少。
再看下他们的真实SQL执行上差距,防止内存或者磁盘溢出,我直接通过count(*)来对比他们的性能:

由此可见,百万数量级直接union的性能比union all distinct的高大概30%。
把recharge充值表数据量提高到千万级别select尝试:

依然是快了30%左右,这绝对是非常实用的一个优化点!