SQL 查询连续月份登录
SQL Query Sequential Month Logins
我有以下SQLtable
username
Month
292
10
123
12
123
1
123
2
123
4
345
6
345
7
我想查询它,以获取每个用户名在连续月份计数中的登录记录。这意味着我正在寻找的最终结果看起来像这样:
username
Streak
292
1
123
3
345
2
如何实现?注意到第 12 个月 --> 第 1 个月的问题;
感谢您的帮助;
一种方法是使用递归 CTE,例如
WITH RECURSIVE cte (username, month, cnt) AS
(
SELECT username, month, 1
FROM test
UNION ALL
SELECT test.username, test.month, cte.cnt+1
FROM cte INNER JOIN test
ON cte.username = test.username AND CASE WHEN cte.month = 12 THEN 1 ELSE cte.month + 1 END = test.month
)
SELECT username, MAX(cnt)
FROM cte
GROUP BY username
ORDER BY username
想法是 CTE(在我的示例中命名为 cte
)在用户相同且月份是下一个的条件下递归地连接回 table。因此对于用户 345,您有:
Username
Month
Cnt
345
6
1
345
7
1
345
7
2
cnt=1 的行来自原始 table(额外的 cnt
列硬编码为 1),cnt=2 的行来自查询的递归部分(它找到了匹配项并使用 cnt+1
作为其 cnt)。然后查询为每个用户选择最大值。
连接使用 CASE
语句来处理 12 后跟 1。
@EdmCoff 分享的那款还是挺优雅的。
另一个没有递归并且只使用条件逻辑的 -
with data_cte as
(
select username, month_1,
case when (count(month_1) over (partition by username) = 1) then 1
when (lead(month_1) over (partition by username order by username) - month_1) = 1 OR (month_1 - lag(month_1) over (partition by username order by username)) = 1 then 1
when (month_1 = 12 and min (month_1) over (partition by username) =1) then 1
end cnt
from login_tab
)
select username, count(cnt) from data_cte group by username
DB Fiddle here.
这会给你想要的结果:
select username, count(*)
from (
select
username
, month_1
, coalesce(nullif(lead(month_1)
over (partition by username
order by coalesce(nullif(month_1,12),0))
- coalesce(nullif(month_1,12),0),-1),1) as MonthsTillNext
from login_tab
) Step1
where MonthsTillNext=1
group by username
通过计算与下一行的差异,其中下一行定义为升序排列的下一个month_no,将12视为0(参考我评论中提到的歧义)。然后它只留下连续月份的行,并对它们进行计数。
但请注意,除了 month:12 周围的异常情况之外,还有另一种情况未被考虑:如果用户的月份是 1、2、3 和 6、7、8,这将算作 Streak:6;是你想要的吗?
我有以下SQLtable
username | Month |
---|---|
292 | 10 |
123 | 12 |
123 | 1 |
123 | 2 |
123 | 4 |
345 | 6 |
345 | 7 |
我想查询它,以获取每个用户名在连续月份计数中的登录记录。这意味着我正在寻找的最终结果看起来像这样:
username | Streak |
---|---|
292 | 1 |
123 | 3 |
345 | 2 |
如何实现?注意到第 12 个月 --> 第 1 个月的问题;
感谢您的帮助;
一种方法是使用递归 CTE,例如
WITH RECURSIVE cte (username, month, cnt) AS
(
SELECT username, month, 1
FROM test
UNION ALL
SELECT test.username, test.month, cte.cnt+1
FROM cte INNER JOIN test
ON cte.username = test.username AND CASE WHEN cte.month = 12 THEN 1 ELSE cte.month + 1 END = test.month
)
SELECT username, MAX(cnt)
FROM cte
GROUP BY username
ORDER BY username
想法是 CTE(在我的示例中命名为 cte
)在用户相同且月份是下一个的条件下递归地连接回 table。因此对于用户 345,您有:
Username | Month | Cnt |
---|---|---|
345 | 6 | 1 |
345 | 7 | 1 |
345 | 7 | 2 |
cnt=1 的行来自原始 table(额外的 cnt
列硬编码为 1),cnt=2 的行来自查询的递归部分(它找到了匹配项并使用 cnt+1
作为其 cnt)。然后查询为每个用户选择最大值。
连接使用 CASE
语句来处理 12 后跟 1。
@EdmCoff 分享的那款还是挺优雅的。
另一个没有递归并且只使用条件逻辑的 -
with data_cte as
(
select username, month_1,
case when (count(month_1) over (partition by username) = 1) then 1
when (lead(month_1) over (partition by username order by username) - month_1) = 1 OR (month_1 - lag(month_1) over (partition by username order by username)) = 1 then 1
when (month_1 = 12 and min (month_1) over (partition by username) =1) then 1
end cnt
from login_tab
)
select username, count(cnt) from data_cte group by username
DB Fiddle here.
这会给你想要的结果:
select username, count(*)
from (
select
username
, month_1
, coalesce(nullif(lead(month_1)
over (partition by username
order by coalesce(nullif(month_1,12),0))
- coalesce(nullif(month_1,12),0),-1),1) as MonthsTillNext
from login_tab
) Step1
where MonthsTillNext=1
group by username
通过计算与下一行的差异,其中下一行定义为升序排列的下一个month_no,将12视为0(参考我评论中提到的歧义)。然后它只留下连续月份的行,并对它们进行计数。
但请注意,除了 month:12 周围的异常情况之外,还有另一种情况未被考虑:如果用户的月份是 1、2、3 和 6、7、8,这将算作 Streak:6;是你想要的吗?