如何将按合同、服务类型分组的求和结果转为单合同单行多列形式?
实现行转列(Pivot)的解决方案
这是典型的行转列需求,我们可以用条件聚合的方式来实现,这种方法几乎在所有SQL数据库中都通用,而且后续新增服务类型时也能轻松扩展。
优化后的查询语句
先给你直接能用的SQL代码,我去掉了原查询里没用到的addresses关联,简化了逻辑:
SELECT c.id, SUM(CASE WHEN s.servicetype_id = 1 THEN cs.price ELSE 0 END) AS total_service_type_1, SUM(CASE WHEN s.servicetype_id = 2 THEN cs.price ELSE 0 END) AS total_service_type_2, SUM(CASE WHEN s.servicetype_id = 3 THEN cs.price ELSE 0 END) AS total_service_type_3, SUM(CASE WHEN s.servicetype_id = 4 THEN cs.price ELSE 0 END) AS total_service_type_4 FROM contracts c LEFT JOIN contracts_services cs ON cs.contract_id = c.id LEFT JOIN services s ON s.id = cs.service_id GROUP BY c.id ORDER BY c.id;
关键逻辑解释
条件聚合核心:
每个SUM(CASE...)语句对应一个服务类型的总和列。当s.servicetype_id匹配目标类型时,取对应的cs.price值,否则取0(如果希望没有对应服务时显示NULL,可以去掉ELSE 0)。通过SUM()把同一合同下该类型的所有价格加总。分组调整:
原查询是按c.id和s.servicetype_id分组,所以每个合同每个服务类型会生成一行。现在我们只按c.id分组,就能让每个合同只返回一行,所有服务类型的总和作为不同列展示。兼容性与扩展性:
这种条件聚合的写法兼容MySQL、PostgreSQL、SQLite、SQL Server等绝大多数数据库。后续新增服务类型时,只需要复制一行SUM(CASE...)语句,修改对应的servicetype_id和列名即可。
可选:特定数据库的PIVOT语法
如果你的数据库支持PIVOT语法(比如SQL Server、Oracle),也可以用更简洁的写法,比如SQL Server版本:
SELECT id, [1] AS total_service_type_1, [2] AS total_service_type_2, [3] AS total_service_type_3, [4] AS total_service_type_4 FROM ( SELECT c.id, s.servicetype_id, cs.price FROM contracts c LEFT JOIN contracts_services cs ON cs.contract_id = c.id LEFT JOIN services s ON s.id = cs.service_id ) AS SourceTable PIVOT ( SUM(price) FOR servicetype_id IN ([1], [2], [3], [4]) ) AS PivotTable ORDER BY id;
不过这种写法的兼容性不如条件聚合,所以更推荐第一种通用方案。
内容的提问来源于stack exchange,提问作者Alex Adm
相关产品推荐
相关产品推荐

