SQL高效计算客户组当年及上年利润总和的实现方案
高效计算客户组年度利润及上年汇总利润的SQL方案
需求说明
现有一张包含customer_group、customer、project、year、profit字段的SQL表,需要为表中每行添加两个字段:
total_profit_current_year:当前customer_group的当年利润总和total_profit_last_year:当前customer_group的上年利润总和(无上年数据时为NULL)
示例输入数据
| customer_group | customer | project | year | profit |
|---|---|---|---|---|
| A | A1 | PA11 | 2018 | 2 |
| A | A2 | PA21 | 2019 | 47 |
| A | A2 | PA21 | 2019 | 12 |
| A | A1 | PA11 | 2020 | 70 |
| B | B1 | PB11 | 2018 | 0 |
| B | B2 | PB21 | 2020 | 5 |
| B | B1 | PB12 | 2021 | 23 |
| C | C1 | PC11 | 2017 | 1 |
| C | C1 | PC12 | 2017 | 4 |
| C | C2 | PC21 | 2018 | 10 |
| C | C2 | PC22 | 2018 | 6 |
| C | C3 | PC33 | 2020 | 11 |
期望输出数据
| customer_group | customer | project | year | profit | total_profit_current_year | total_profit_last_year |
|---|---|---|---|---|---|---|
| A | A1 | PA11 | 2018 | 2 | 2 | NULL |
| A | A2 | PA21 | 2019 | 47 | 59 | 2 |
| A | A2 | PA21 | 2019 | 12 | 59 | 2 |
| A | A1 | PA11 | 2020 | 70 | 70 | 59 |
| B | B1 | PB11 | 2018 | 0 | 0 | NULL |
| B | B2 | PB21 | 2020 | 5 | 5 | NULL |
| B | B1 | PB12 | 2021 | 23 | 23 | 5 |
| C | C1 | PC11 | 2017 | 1 | 5 | NULL |
| C | C1 | PC12 | 2017 | 4 | 5 | NULL |
| C | C2 | PC21 | 2018 | 10 | 16 | 5 |
| C | C2 | PC22 | 2018 | 6 | 16 | 5 |
| C | C3 | PC33 | 2020 | 11 | 11 | NULL |
问题背景
- 尝试过
LAG()窗口函数:仅能获取单条记录的上年利润,无法统计整个客户组的上年总利润。 - 尝试过WITH子句汇总后两次左连接:在百万级数据量下性能极差,多次扫描原表导致效率低下。
高效解决方案
方案思路
- 先通过一次汇总计算出每个客户组、每个年份的总利润,并利用
LAG()窗口函数获取该客户组的上年总利润。 - 将汇总结果与原表进行一次左连接,把两个汇总字段关联到原表的每一行。
这种方式仅需对原表进行一次扫描汇总,再进行一次关联操作,大幅减少IO开销,适合处理百万级数据。
SQL代码实现
WITH yearly_group_profit AS ( SELECT customer_group, year, SUM(profit) AS total_profit_current_year, LAG(SUM(profit)) OVER (PARTITION BY customer_group ORDER BY year) AS total_profit_last_year FROM your_table_name GROUP BY customer_group, year ) SELECT t.*, y.total_profit_current_year, y.total_profit_last_year FROM your_table_name t LEFT JOIN yearly_group_profit y ON t.customer_group = y.customer_group AND t.year = y.year;
优化说明
- 如果你的SQL引擎支持(如PostgreSQL、MySQL 8.0+、SQL Server等),可以考虑在
customer_group和year字段上建立联合索引,进一步提升分组和关联的效率:CREATE INDEX idx_customer_group_year ON your_table_name(customer_group, year);
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

