select
s.merchant_id merchant_id,
merchant_name,
total_sales_amount,
total_refund_amount,
average_satisfaction_score
from merchants_underline m
join (
select merchant_id,
sum(sale_amount) total_sales_amount
from sales_underline
group by merchant_id
) s on m.merchant_id=s.merchant_id
join(
select merchant_id,
sum(refund_amount) total_refund_amount
from refunds_underline
group by merchant_id
) r on s.merchant_id=r.merchant_id
join(
select merchant_id,
round(avg(satisfaction_score),2) average_satisfaction_score
from satisfaction_underline
group by merchant_id
)f on r.merchant_id=f.merchant_id
order by merchant_id
不能直接简单粗暴四表相连,容易出现笛卡尔积,导致多加,因此应该逐表相连,联结表的过程中做好添加列的筛选和计算
这段 SQL 选择分步子查询 + 连接而非直接四表连接,主要因多表聚合逻辑冲突与性能问题,以下是关键原因:
1. 聚合逻辑冲突:多对多关系导致重复计算
假设直接四表连接(merchants_underline 连 sales_underline、refunds_underline、satisfaction_underline),若:
sales_underline中某商家有 2 条销售记录refunds_underline中该商家有 3 条退款记录
直接四表连接会产生 2×3=6 条重复行,导致:
sum(sale_amount)被重复计算 3 次(实际应只算 2 次)sum(refund_amount)被重复计算 2 次(实际应只算 3 次)
最终聚合结果完全错误。
2. 分步聚合:先分组再连接,避免重复
当前写法的逻辑是:
- 子查询 1:按商家聚合销售总额(
sales_underline→ 1 行 / 商家) - 子查询 2:按商家聚合退款总额(
refunds_underline→ 1 行 / 商家) - 子查询 3:按商家聚合平均满意度(
satisfaction_underline→ 1 行 / 商家) - 最后连接:基于
merchant_id关联,确保 1 行 / 商家,聚合结果正确。
3. 直接四表连接的隐患
若强行直接四表连接(不分组),需处理:
sql
select m.merchant_id, sum(s.sale_amount) total_sales_amount, -- 因多对多,sum 会重复计算 sum(r.refund_amount) total_refund_amount, avg(f.satisfaction_score) average_satisfaction_score from merchants_underline m join sales_underline s on m.merchant_id = s.merchant_id join refunds_underline r on m.merchant_id = r.merchant_id join satisfaction_underline f on m.merchant_id = f.merchant_id group by m.merchant_id;
- 问题:多表的
merchant_id形成多对多关系,导致sum/avg因重复行计算错误。 - 极端情况:若某商家在 3 张表中各有 10 条记录,直接连接会产生
10×10×10=1000行,聚合结果放大 100 倍。
4. 性能差异:分步聚合更高效
- 分步子查询:每张表单独聚合(扫描一次),结果集小(1 行 / 商家),后续连接成本低。
- 直接四表连接:需先产生巨大中间结果(多对多膨胀),再聚合,性能可能差几个数量级。
总结:何时能用直接四表连接?
只有当所有表与主表是 1:1 或 1:0/1 关系(无多对多)时,直接四表连接才安全。
而本例中 sales、refunds、satisfaction 均是多对一(多记录对应 1 商家),必须先分组聚合,再连接,否则结果错误。

京公网安备 11010502036488号