使用 SQL 聚合基于不同月份的年度数据

Posted

技术标签:

【中文标题】使用 SQL 聚合基于不同月份的年度数据【英文标题】:Aggregate yearly data based on different months using SQL 【发布时间】:2021-01-25 04:57:24 【问题描述】:

我有一个“金额”表,其中包含客户的每月付款。每个客户的每笔付款都有一行。我想从他们第一次支付的月份开始每年汇总他们的付款。例如,在下表中,对于用户 ID 132,我想将他从 2019 年第 9 个月到 2020 年第 8 个月(一整年)的付款汇总为一行,然后从 2020 年第 9 个月到下一个汇总为另一行行。

基本上,我希望根据他们加入的月份将每年的用户数量作为行。我不确定如何使用 SQL 聚合这些数据,希望能在此提供帮助。

示例表(如果更简单,我可以将年月列合并为原始数据本身的日期列)>

+--------+------+-------+--------+
| userID | year | month | amount |
+--------+------+-------+--------+
| 132    | 2019 | 9     | 836    |
+--------+------+-------+--------+
| 132    | 2019 | 10    | 702    |
+--------+------+-------+--------+
| 132    | 2019 | 11    | 161    |
+--------+------+-------+--------+
| 132    | 2019 | 12    | 955    |
+--------+------+-------+--------+
| 132    | 2020 | 1     | 969    |
+--------+------+-------+--------+
| 132    | 2020 | 2     | 977    |
+--------+------+-------+--------+
| 132    | 2020 | 3     | 986    |
+--------+------+-------+--------+
| 132    | 2020 | 4     | 639    |
+--------+------+-------+--------+
| 132    | 2020 | 5     | 411    |
+--------+------+-------+--------+
| 132    | 2020 | 6     | 302    |
+--------+------+-------+--------+
| 132    | 2020 | 7     | 929    |
+--------+------+-------+--------+
| 132    | 2020 | 8     | 884    |
+--------+------+-------+--------+
| 132    | 2020 | 9     | 644    |
+--------+------+-------+--------+
| 132    | 2020 | 10    | 640    |
+--------+------+-------+--------+
| 132    | 2020 | 11    | 121    |
+--------+------+-------+--------+
| 132    | 2020 | 12    | 980    |
+--------+------+-------+--------+
| 1447   | 2020 | 11    | 356    |
+--------+------+-------+--------+
| 1447   | 2020 | 12    | 351    |
+--------+------+-------+--------+

样本输出(这里的年周期列只是为了表明自用户加入以来总数属于哪一年)。>

+--------+------------+----------------------+
| userID | Year Cycle | Current Total Amount |
+--------+------------+----------------------+
| 132    | 1          | 8751                 |
+--------+------------+----------------------+
| 132    | 2          | 2385                 |
+--------+------------+----------------------+
| 1447   | 1          | 707                  |
+--------+------------+----------------------+

【问题讨论】:

请添加tag of your DBMS。并且不要使用数据图像,将其粘贴为纯文本。 按年、月从表组中选择总和(值)、年、月 @astentx 谢谢。我已按照您的建议进行了修改。 @WalterVehoeven 我认为这不会给我想要的结果。我想要一行表示前 12 个月的总和(值),然后另一行表示接下来 12 个月的总和,基于每个用户的数据开始的月份。 您需要向我们展示您的预期结果和尝试。 【参考方案1】:

参考资料 -

    Common Table Expressions (CTE), https://www.sqlshack.com/sql-server-common-table-expressions-cte/ SELECT - OVER Clause

使用CTE 如下所示获得您想要的结果。

获取日期列以获取具有第一个 CTE AmountWithDate 的每个用户的最短日期。

然后使用之前的CTE AmountWithDate 创建另一个CTE AmountWithYearDifference 以从用户的第一笔交易中获取years。必须使用over(partition by ...)

然后使用AmountWithYearDifferenceGROUP BYuserid & dtSUM(Amount) 以获得所需的输出。

;WITH AmountWithDate AS (
    SELECT *, DATEFROMPARTS(year, month, 1) AS dt 
    FROM Amounts
)
, AmountWithYearDifference AS (
    SELECT *, DATEDIFF(MONTH, (min(dt) over(partition by userid)), dt) / 12 AS years
    FROM AmountWithDate
)
SELECT userid, 
        min(dt) AS dt, DATEPART(MONTH, MIN(dt)) as month, 
        DATEPART(YEAR, MIN(dt)) as year, 
        sum(Amount)
FROM AmountWithYearDifference
GROUP BY userid, years

编辑如果您已经拥有date 列,则无需先拥有CTE,您可以直接使用第二个CTE。我假设您的date 列名是dt

;WITH AmountWithYearDifference AS (
    SELECT *, DATEDIFF(MONTH, (min(dt) over(partition by userid)), dt) / 12 AS years
    FROM Amounts
)
SELECT userid, 
        min(dt) AS dt, DATEPART(MONTH, MIN(dt)) as month, 
        DATEPART(YEAR, MIN(dt)) as year, 
        sum(Amount)
FROM AmountWithYearDifference
GROUP BY userid, years

如果您不熟悉CTE 并希望使用inner sql query,那么您可以编写如下查询。只需在inner query 中从CTE 写入query,如下所示。

SELECT userid, 
        min(dt) AS dt, DATEPART(MONTH, MIN(dt)) as month, 
        DATEPART(YEAR, MIN(dt)) as year, 
        sum(Amount)
FROM (
    SELECT *, DATEDIFF(MONTH, (min(dt) over(partition by userid)), dt) / 12 AS years
    FROM Amounts
)
GROUP BY userid, years

【讨论】:

嘿!谢谢你。我是 SQL 新手,所以我不太熟悉 CTE。我看到您正在从第一个 CTE 创建一个日期列。如果我已经在基表中创建了所需的日期列,而不是月份和年份的列,是否会使工作更容易? 绝对有日期列将是一个优势。您只需要一个CTE。我添加了一些链接供参考。 CTE 是一种临时表。【参考方案2】:

您可以使用row_number()为每个用户生成一个序列一个数字,然后将每12个分组为1个循环

select userId, cycle, sum(amount)
from
(
    select *, 
           cycle = (row_number() over (partition by userId 
                                           order by year, month) - 1) / 12 + 1
    from   Amounts
) t
group by userId, cycle

db<>fiddle demo

【讨论】:

如果OPone and only one 记录为each month after joining 那么只有这个代码可以工作。

以上是关于使用 SQL 聚合基于不同月份的年度数据的主要内容,如果未能解决你的问题,请参考以下文章

基于会话的 Google Big Query SQL 聚合数据

MySQL聚合函数

在 Presto 中基于固定移动日期窗口聚合数据

pandas使用groupby函数基于指定分组变量对dataframe数据进行分组使用agg函数计算每个分组不同数值变量的聚合统计值agg参数为字典指定不同变量的聚合计算统计量的形式

基于unix时间戳聚合数据创建数据库

SQL聚合中同一列内的多个值的不同计数