MySQL中如何用CASE语句将多行查询结果合并为单行
优化方案:用条件聚合实现单行列转行
当然可以,不需要用多次左连接,条件聚合是更高效的解决方案,能直接把每个供应商的12个月数据合并成单行。
原查询的问题分析
你当前的查询生成多行结果的核心原因是GROUP BY v.id,m(按供应商+月份分组),导致每个供应商的每个交易月份单独成一行;同时CASE和SUM的嵌套逻辑颠倒,无法实现跨月份的聚合。
优化后的SQL代码
SELECT v.id, v.name, COALESCE(SUM(CASE WHEN MONTH(g.trans_date) = 1 THEN g.debit - g.credit END), 0) AS jan, COALESCE(SUM(CASE WHEN MONTH(g.trans_date) = 2 THEN g.debit - g.credit END), 0) AS feb, COALESCE(SUM(CASE WHEN MONTH(g.trans_date) = 3 THEN g.debit - g.credit END), 0) AS mar, COALESCE(SUM(CASE WHEN MONTH(g.trans_date) = 4 THEN g.debit - g.credit END), 0) AS apr, COALESCE(SUM(CASE WHEN MONTH(g.trans_date) = 5 THEN g.debit - g.credit END), 0) AS may, COALESCE(SUM(CASE WHEN MONTH(g.trans_date) = 6 THEN g.debit - g.credit END), 0) AS jun, COALESCE(SUM(CASE WHEN MONTH(g.trans_date) = 7 THEN g.debit - g.credit END), 0) AS jul, COALESCE(SUM(CASE WHEN MONTH(g.trans_date) = 8 THEN g.debit - g.credit END), 0) AS aug, COALESCE(SUM(CASE WHEN MONTH(g.trans_date) = 9 THEN g.debit - g.credit END), 0) AS sept, COALESCE(SUM(CASE WHEN MONTH(g.trans_date) = 10 THEN g.debit - g.credit END), 0) AS oct, COALESCE(SUM(CASE WHEN MONTH(g.trans_date) = 11 THEN g.debit - g.credit END), 0) AS nov, COALESCE(SUM(CASE WHEN MONTH(g.trans_date) = 12 THEN g.debit - g.credit END), 0) AS dec FROM vendor_table AS v LEFT JOIN transaction_details AS g ON g.vendor = v.id AND g.CODE IN ('dr#') AND g.account IN (20) AND YEAR(g.trans_date) = 2022 GROUP BY v.id, v.name;
关键优化点
- 调整过滤条件位置:把原WHERE中属于
transaction_details的筛选条件移到LEFT JOIN的ON子句中,避免过滤掉没有交易记录的供应商(否则LEFT JOIN会自动退化为INNER JOIN)。 - 修改分组逻辑:GROUP BY仅按供应商的
id和name分组,确保每个供应商只返回一行数据。 - 条件聚合逻辑:用
SUM(CASE ...)实现分月求和——对每个月份符合条件的交易金额单独聚合,没有数据的月份通过COALESCE将NULL转为0。
效率优势
这种方案只需要扫描vendor_table和transaction_details各一次,相比12次左连接的方案,彻底避免了重复扫描交易表的开销,在数据量较大时性能提升非常显著。
内容的提问来源于stack exchange,提问作者melvnberd
相关产品推荐
相关产品推荐

