如何在SQL查询中按合同起始年度逐年统计应付金额并以列形式展示
解决方案:按合同年度偏移统计应付金额并转列
哈哈,这个需求我上周刚帮同事处理过!本质就是把按合同年度聚合的结果行转列,核心是先搞定每个金额对应的是合同的第几个年度,再用条件聚合把不同年度的总和拆成单独的列。我给你两种常用的实现方式,基本适配所有主流SQL方言~
首先得明确几个前提(如果你的表结构不一样,稍微改改字段名就行):
假设我们有个表叫contract_payments,存了每个合同的年度应付记录,字段包括:
contract_id:合同唯一IDstart_date:合同起始日期(DATE类型)payment_date:该笔应付对应的日期(用来判断属于合同的第几个年度)due_amount:该年度的应付金额
方法1:通用条件聚合(所有SQL方言都支持)
这是最稳妥的方式,用CASE WHEN配合SUM实现条件聚合,把不同年度的总和映射到单独的列:
SELECT -- 合同第1年:付款年份 = 合同起始年份 SUM(CASE WHEN YEAR(start_date) = YEAR(payment_date) THEN due_amount ELSE 0 END) AS annual_amount_1, -- 合同第2年:付款年份 = 起始年份 + 1 SUM(CASE WHEN YEAR(start_date) + 1 = YEAR(payment_date) THEN due_amount ELSE 0 END) AS annual_amount_2, -- 合同第3年:付款年份 = 起始年份 + 2 SUM(CASE WHEN YEAR(start_date) + 2 = YEAR(payment_date) THEN due_amount ELSE 0 END) AS annual_amount_3 FROM contract_payments -- 可选:过滤掉期限超过3年的合同 WHERE YEAR(end_date) - YEAR(start_date) <= 2;
关键逻辑解释:
- 用
YEAR()函数提取日期的年份(如果你的SQL方言不支持,比如Oracle用EXTRACT(YEAR FROM date)替代) - 通过年份差判断该笔应付属于合同的第几个年度:差为0是第1年,差为1是第2年,以此类推
CASE WHEN会把符合条件的due_amount保留,不符合的设为0,再用SUM汇总所有符合条件的金额
方法2:先拆分年度再聚合(适合总金额分摊场景)
如果你的表是存的合同总应付金额(不是年度拆分后的),需要先把总金额平摊到每一年,再统计总和:
SELECT SUM(CASE WHEN contract_year_offset = 0 THEN annual_due ELSE 0 END) AS annual_amount_1, SUM(CASE WHEN contract_year_offset = 1 THEN annual_due ELSE 0 END) AS annual_amount_2, SUM(CASE WHEN contract_year_offset = 2 THEN annual_due ELSE 0 END) AS annual_amount_3 FROM ( -- 子查询:拆分每个合同的年度分摊金额和年度偏移 SELECT contract_id, start_date, end_date, -- 把总金额平摊到每一年 due_amount / (YEAR(end_date) - YEAR(start_date) + 1) AS annual_due, -- 生成年度偏移(0=第1年,1=第2年...) generate_series(0, YEAR(end_date) - YEAR(start_date)) AS contract_year_offset FROM contracts ) AS contract_annual_split;
注意事项:
generate_series是PostgreSQL的函数,MySQL可以用递归CTE替代生成年度偏移;SQL Server用ROW_NUMBER()配合递归- 如果合同是按生效后12个月为一个年度(不是自然年),那不能直接用年份差,得用月份差计算:
-- 按合同生效后12个月为年度的判断逻辑 SUM(CASE WHEN FLOOR(DATEDIFF(MONTH, start_date, payment_date)/12) = 0 THEN due_amount ELSE 0 END) AS annual_amount_1
内容的提问来源于stack exchange,提问作者Abinnaya
相关产品推荐
相关产品推荐

