我有以下数据:
date unit status
2023-04-30 unit1 1
2023-05-31 unit1 1
2023-08-31 unit1 1
2023-09-30 unit1 1
2023-11-30 unit1 1
2023-12-31 unit1 1
2024-01-31 unit1 1
2024-02-28 unit1 1
作为参考日期,我想知道第一个即将到来的“streak”的长度(在 MSSQL 上,用于生产,在 sqlite 上,用于单元测试)
对于日期2023-05-15我想要的输出是:
unit streak
unit1 3
这样做的原因是2023-05之后第一个月status=1是2023-08,然后我只统计每个连续的月份。
对于日期2023-11-01我想要的输出是:
unit streak
unit1 3
原因是 2023-11 后第一个 status=1 的月份是 2023-12,并且连续在 2024-02 结束,因为 status=0 的月份没有记录,下一个 status=1 的月份多于还有一个月。
对于 SQL Server 和 SQLite,计算 streak 需要结合使用公共表表达式 (CTE)、窗口函数和联接。让我们来解决这个问题:
SQL Server 解决方案:
WITH RankedData AS (
SELECT [date], [unit], [status],
ROW_NUMBER() OVER (PARTITION BY [unit] ORDER BY [date]) -
MONTH([date]) AS GroupingID
FROM YourTableName
WHERE [date] > '2023-05-15' AND [status] = 1
)
SELECT TOP 1 [unit], COUNT(*) AS streak
FROM RankedData
GROUP BY [unit], GroupingID
ORDER BY MIN([date]);
SQLite 解决方案: SQLite 缺乏 SQL Server 的一些高级窗口功能,但您可以通过连接和子查询实现类似的效果:
WITH RankedData AS (
SELECT [date], [unit], [status],
strftime('%m', [date]) + 0 - (ROW_NUMBER() OVER (PARTITION BY [unit] ORDER BY [date])) AS GroupingID
FROM YourTableName
WHERE [date] > '2023-05-15' AND [status] = 1
)
SELECT [unit], COUNT(*) AS streak
FROM RankedData
GROUP BY [unit], GroupingID
ORDER BY MIN([date])
LIMIT 1;
这些脚本应提供给定参考日期的第一个即将到来的连续的长度。只需根据需要调整 WHERE 子句中的日期即可。
这是一个
gaps and islands
问题,可以通过(值减去row_number)来解决,因为它在连续序列中是不变的。开始和结束日期只是该组的 MIN() 和 MAX() :
with cte as (
SELECT *, GroupingSet = FORMAT(DATEADD(
MONTH, - ROW_NUMBER() OVER(PARTITION BY unit ORDER BY [date]),
[date]
), 'yyyy-MM-01')
FROM mytable
WHERE MONTH([date]) > MONTH('2023-05-15') AND [status] = 1
)
SELECT TOP 1 unit,
StartDate = MIN([date]),
EndDate = MAX([date]),
streak = COUNT(*)
FROM CTE
GROUP BY unit, GroupingSet
ORDER BY StartDate;
注意:给出的日期已转换为该月的第一天,因此 GroupingSet 可以在同一个月内匹配!