首页 >

某乎问答最大连续回答问题天数大于等于3天的用户及其对应等级

连续天数:日期减排名,差值分组法
with t1 as(
    select
        author_id,author_level,answer_date,
        date_sub(answer_date,interval dense_rank()over(partition by at.author_id order by answer_date) day) as same
    from answer_tb at
        inner join author_tb using(author_id)
)
select
    author_id,
    author_level,
    count(distinct(answer_date)) as days_cnt
from t1 
group by 1,2,same
having days_cnt>=3
order by 1


连续登陆天数问题,依然使用当前日期减去排序编号差值相等来解决
  • 首先,由于一个用户可能在同一天活跃多次,这样同一天的记录也有多次
所以在编号时使用DENSE_RANK():并列排序,不跳过重复序号——1、1、2
select
*,
dense_rank() over(partition by author_id 
                  order by answer_date) as rn
from answer_tb
  • 将上表命名为temp1,使用上表,计算差值
with temp1 as 
(
select
*,
dense_rank() over(partition by author_id 
                  order by answer_date) as rn
from answer_tb a
)
select
author_id,
answer_date,
date_sub(answer_date,interval rn day) as dt
from temp1
这样就得到了一个日期和日期减编号(dt)的表,
dt相等表示同一天登陆
注意这里还有一个问题,就是同一个用户有多次同一天的记录,这时差值也相等
  • 我们接下来这样解决
select
author_id,
count(distinct answer_date) as days_cnt
from temp2
group by author_id,dt
having days_cnt >=3
先按照作者,和差值日期(dt)分组,然后对answer_date去重计数
这样同一个用户有多次同一天的记录就只计一次
  • 由于题目还要输出author_level,所以我们再利用上表左连接用户信息表
选中author_level列即可
select
distinct author_id,
author_level,
days_cnt
from
(
select
author_id,
count(distinct answer_date) as days_cnt
from temp2
group by author_id,dt
having days_cnt >=3
) t1
left join author_tb t2 using(author_id)
order by author_id
  • 完整的代码如下
with temp1 as 
(
select
*,
dense_rank() over(partition by author_id 
                  order by answer_date) as rn
from answer_tb
),
temp2 as 
(
select
author_id,
answer_date,
date_sub(answer_date,interval rn day) as dt
from temp1
)

select
distinct author_id,
author_level,
days_cnt
from
(
select
author_id,
count(distinct answer_date) as days_cnt
from temp2
group by author_id,dt
having days_cnt >=3
) t1
left join author_tb t2 using(author_id)
order by author_id
  • 但是这个解法只考虑了符合条件的用户只有一次连续签到天数>=3的情况
我感觉题目的意思也是只考虑了这个情况。
出现符合条件的用户有多次连续签到天数>=3的情况
我觉得题目的要求可能就是需要输出连续签到天数最大值
  • 如果出现符合条件的用户有多次连续签到天数>=3的情况
我有个不成熟的思路 
就是在以上生成的表的基础上,使用group by author_id 并且在select列里添加Max(days_cnt) 这样就选出了符合条件的用户的最大连续签到天数(>=3
  • 如有问题欢迎批评指正
发表于 2022-05-16 21:26:19 回复(3)
这是我认为最严谨的写法
select t2.author_id,t2.author_level,max(ct)
from (select id,count(dts) ct
from(select id,dt,date_sub(dt,interval diff day) dts
from (SELECT answer_date dt,author_id id,ROW_NUMBER() over(partition by author_id order by answer_date) diff
    from(SELECT distinct answer_date,author_id
        from answer_tb) s) ss) sss
group by id,dts
having count(dt) >=3
) t1
join author_tb t2
on t1.id = t2.author_id
group by t2.author_id,t2.author_level
order by t2.author_id
发表于 2022-02-19 13:22:34 回复(2)
# 实现原理
/*
连续登陆,也就是在连续登陆的期间内,后一天和前一天的差值为1,不能为大于1的值,
直到间断。那么在这里其实我们可以设置一列序号,如果是连续的话,
这列序号也是会随着日期同步增长的,那么日期减去这个序号,应该都是一个确定的日期。
比如说9月16号对应的序号是1,9月17号对应的序号是2,9月18号对应的序号是3,
那么9月16号-1=9月15号,同理,9月17号-2=9月15号,都是同样的日期。
那么我们根据这个日期和user_id和日期标示进行分组即可。
*/
# step1:添加新列
# step2:做差
# step3:id进行分组,统计次数
select                                             #step3:进行分组,统计次数
    t.author_id,t.author_level,
    count(diff_date) as days_cnt
from
(
select
    author_id,
    answer_date,
    date_sub(answer_date,interval rn day) as diff_date   #step2:做差
from
    (select author_id,answer_date,
        ROW_NUMBER()over(partition by author_id order by answer_date) as rn
     from answer_tb) a                             #step1:添加新列
) b 
join author_tb t on b.author_id=t.author_id
group by t.author_id,diff_date
having days_cnt >=3;
发表于 2021-12-04 14:25:22 回复(1)
首先对每个author id进行编号,用日期减去编号,如果减出来是相同的值则可以认为是连续登陆的天数,比如一用户登陆日期为10.1、10.2、10.3、10.5、10.7编号依次为1、2、3、4、5,那么日期减编号则为0、0、0、1、2,再根据author ID和减出来的值进行分组,此时count就是连续登录天数 再having连续天数》=3即可
select author_id,author_level,count(*)days_cnt from
(select * ,(date(answer_date)-a)b from 
 (select author_id,author_level,answer_date,row_number()over(partition by author_id )a
from answer_tb join author_tb using(author_id))c)d
group by author_id,author_level,b
having days_cnt>=3;

发表于 2022-04-26 18:56:48 回复(1)
SELECT
    t2.author_id,
    author_level,
    MAX(lx_cnt) AS days_cnt
FROM
    (SELECT
        author_id,
        DATE_SUB(answer_date, INTERVAL rk DAY) AS fz_dt,
        COUNT(answer_date) AS lx_cnt
    FROM
        (SELECT DISTINCT
            author_id,
            answer_date,
            DENSE_RANK()OVER(PARTITION BY author_id ORDER BY answer_date) AS rk
        FROM answer_tb) AS t1
        GROUP BY author_id, fz_dt) AS t2
    JOIN author_tb AS t3
    ON t2.author_id = t3.author_id
GROUP BY t2.author_id, author_level
HAVING days_cnt >= 3
ORDER BY t2.author_id;

发表于 2022-01-09 16:39:40 回复(1)
with t1 as (
    select distinct
        author_id,
        answer_date,
        dense_rank() over(partition by author_id order by answer_date) rn
    from answer_tb
),
t2 as (
    select
        author_id,
        count(answer_date) days_cnt
    from t1
    group by author_id,date_sub(answer_date,interval rn day)
)
select
    a.author_id,
    author_level,
    max(days_cnt) max_days
from t2 join author_tb a on t2.author_id = a.author_id
group by a.author_id,author_level
having max_days >= 3
order by a.author_id;

发表于 2023-08-05 22:06:17 回复(2)
利用窗口函数lead()获取回答日期的下一个挨着的日期,如果这两个日期相隔一天(timestampdiff函数),则满足相邻条件,最后按照author_id将符合的行数汇总求和再+1则得到连续回答问题最大天数
select b.author_id,author_level,count(b.author_id)+1 days_cnt from 
(select author_id, answer_date, lead(answer_date,1) over (partition by author_id order by answer_date) d2
from (select distinct answer_date,author_id from answer_tb) a ) b left join author_tb c
on b.author_id=c.author_id
where timestampdiff(day,answer_date,d2)=1
group by author_id
order by author_id


发表于 2022-01-05 14:38:51 回复(2)
select l.author_id, author_level, count(diff) as days_cnt 
from(select author_id, answer_date, answer_date-r as diff 
     from(select author_id,answer_date, 
        row_number() over(partition by author_id order by answer_date)as r
          from (select author_id, answer_date 
                from answer_tb
                group by author_id,answer_date)t)t1)t2
join author_tb l on t2.author_id=l.author_id
group by author_id,diff
having count(diff)>=3

# 0 必须去重。
    # 不然如果日期是11.1(排序1)11.2(2)11.2(3)11.3(4)这样连续三天回答,
    # 可是作差后相同的只有2个,后面group之后选count>=3的就不会算进去。
# 1 添加row_number列对日期编号,按分组排序。
# 2 日期-row_number能得到连续回答的前一天,是一个固定的值,如11.1-1=11.2-2=11.3-3!=11.11-4
# 3 检查连续日期大于3个:日期-日期对应的行数,所求出的是一样的值的>3个
发表于 2021-12-17 16:14:14 回复(2)
select c.author_id
,author_level
,max(days_cnt) days_cnt
from(
    select b.author_id
    ,author_level
    ,count(tim) days_cnt
    from
    (
        -- (当一组日期按照升序排序时,如果某几个日期是连续的,它们减去本身的DENSE RANK,会发现都是相减的结果都是同一天)
        select a.author_id
        ,author_level
        ,answer_date
        ,cast(day(answer_date) AS SIGNED) - dense_rank() over(partition by author_id order by answer_date) tim
        from(
            select
            t1.author_id
            ,author_level
            ,answer_date
            from
            author_tb t1 join answer_tb t2 using(author_id) -- using和on的作用一样,相当于简写
            group by 1,2,3 -- 虽然写的是123,但是本质上是对第三列进行去重 / 也可以替换为 去掉这行 在answer_date前加入distinct
            order by 1,3 -- 按照作者、日期进行升序排序
        ) a
    ) b
    group by 1,2,tim -- 核心在这里
    having days_cnt >= 3
) c
group by 1,2;

发表于 2023-08-09 18:57:02 回复(0)
with t3 as(select distinct author_id,answer_date,dense_rank() over(partition by author_id order by answer_date) as ranking
                from answer_tb),
       t4 as(select author_id,date_add(answer_date,interval -ranking day) as constant_answer
                from t3)

select t4.author_id,max(author_level),count(constant_answer) as days_cnt
from t4 inner join author_tb as t1 on t4.author_id=t1.author_id
group by t4.author_id,constant_answer
having days_cnt>=3
order by t4.author_id;
发表于 2022-05-11 17:28:56 回复(0)
select author_id, author_level, max(days_cnt)
from
(select d.author_id, author_level, count(diff) days_cnt
from
(select author_id, answer_date, answer_date - ranking diff
from
(select author_id, answer_date,
        row_number() over (partition by author_id order by answer_date) ranking
from
(select author_id, answer_date from answer_tb group by author_id, answer_date) a) b) c
join author_tb d on c.author_id = d.author_id
group by author_id,author_level, diff
having days_cnt >= 3) e
group by author_id, author_level
发表于 2022-03-09 21:28:32 回复(1)
WITH t AS (
    SELECT author_id, answer_date,
           rank() over(PARTITION BY author_id ORDER BY answer_date) AS rk,
           date_sub(answer_date, INTERVAL rank() over(PARTITION BY author_id ORDER BY answer_date) day) AS begin_date
    FROM answer_tb at2
    GROUP BY author_id, answer_date 
    ORDER BY author_id, answer_date 
)
SELECT t2.author_id, author_level, max(cnt) AS days_cnt
FROM (
    SELECT author_id, begin_date, count(1) AS cnt 
    FROM t 
    GROUP BY author_id, begin_date
    HAVING count(1) >= 3) t2
LEFT JOIN author_tb a ON t2.author_id = a.author_id 
GROUP BY t2.author_id, author_level 
ORDER BY t2.author_id;

发表于 2024-10-16 17:18:51 回复(0)
select i,author_level,count(l) o
from(select i,d,date_sub(d,interval p day) l
    from(select author_id i,answer_date d
        ,row_number()over(partition by author_id order by answer_date) p 
        from answer_tb)a)b
join author_tb t on b.i=t.author_id
group by 1,2,l
having o>=3;

发表于 2024-08-04 20:38:53 回复(0)
with t1 as (
    select distinct author_id,answer_date
    from answer_tb
),
t2 as (select author_id,answer_date,row_number()over(partition by author_id order by answer_date) rank_num
from t1),
t3 as (select author_id,date_sub(answer_date,interval rank_num day) dt
from t2)
select author_id,author_level,count(dt)
from t3
left join author_tb using(author_id)
group by author_id,author_level
having count(dt) >= 3
发表于 2024-05-18 17:46:44 回复(0)
重点在于“最大连续回答问题天数大于等于3天”的筛选
将登陆表自连接得到3张同一个表拼接的合并表,对连接条件进行筛选:三表日期是连续的三天即可。假设1 2  3代表连续的第1、2、3天。则符合要求的连接方式有:
1 2 3
1 3 2
2 1 3 
2 3 1
3 1 2
3 2 1
共6种连接方式,如何选择?根据题意,我们要计算用户连续登陆的天数,使用count(表名.字段)的方式进行计算,所以必须把连续登陆的所有日期查询出来,我们选择最左侧的表来计算,因此,最左侧表的answer_date字段,必须包含连续回答问题的所有日期。有了这个条件,我们在6个连接条件中选择开头不重复的三组即可,这三组连接方式可覆盖任意连续的三天。比如 1 2 3、2 3 1、3 1 2,左侧表的日期就包含了连续三天中的第1、2、3天。
代码:
select
    t4.author_id,
    t4.author_level,
    t5.cnt as days_cnt
from
    author_tb as t4,
    (
        select distinct
            t1.author_id as author_id,
            count(distinct t1.answer_date) as cnt
        from
            answer_tb as t1
            join answer_tb as t2
            join answer_tb as t3 on t1.author_id = t2.author_id
            and t2.author_id = t3.author_id
            and (
                # 1 2 3
                (
                    timestampdiff (day, t1.answer_date, t2.answer_date) = -1
                    and timestampdiff (day, t2.answer_date, t3.answer_date) = -1
                )
                #2 1 3
               &nbs***bsp;(
                    timestampdiff (day, t1.answer_date, t2.answer_date) = 1
                    and timestampdiff (day, t2.answer_date, t3.answer_date) = -2
                )
                # 3 1 2
               &nbs***bsp;(
                    timestampdiff (day, t1.answer_date, t2.answer_date) = 2
                    and timestampdiff (day, t2.answer_date, t3.answer_date) = -1
                )
            )
        group by
            author_id
    ) as t5
where
    t4.author_id = t5.author_id;


发表于 2023-09-18 03:15:31 回复(0)
牛客网的Mysql是8.0,MySQL 用户变量在8.0.13后不可用。
这题仍然可以使用用户变量过程编程的方式解决"连续天数"的问题。

# Keep in mind that MySQL user variables are deprecated since MySQL 8.0.13, so using window functions might be a better approach for future-proofing your query.

WITH answer_dates AS (
    SELECT
        answer_date,
        author_id,
        IF(answer_date = @prev_date + INTERVAL 1 DAY AND author_id = @prev_author_id, @consec_days := @consec_days + 1, @consec_days := 1) AS consec_days,
        @prev_date := answer_date,
        @prev_author_id := author_id
    FROM
        (SELECT @prev_date := NULL, @prev_author_id := NULL, @consec_days := 1) vars,
        (SELECT answer_date, author_id FROM answer_tb ORDER BY author_id, answer_date) ordered_dates
)


select t1.author_id,t2.author_level as author_level,
CAST(t1.max_consec_days AS UNSIGNED) AS days_cnt
from
(
    SELECT
        author_id,
        MAX(consec_days) AS max_consec_days
    FROM
        answer_dates
    GROUP BY
        author_id
    HAVING
        max_consec_days >= 3
)t1
join author_tb t2
on t2.author_id=t1.author_id


发表于 2023-03-22 21:34:42 回复(0)

【场景】:连续记录

【分类】:分组条件查询、窗口函数

分析思路

难点:

1.如何表示连接?连续的表示:日期减去排序的值相等

2.记得先对日期、用户去重

(1)对日期、创作者进行去重

  • [使用]:distinct

(2)统计日期、创作者、日期减去排序的值(连续签到)

连续的表示:日期减去排序的值相等;

  • [使用]:date(in_time) - row_number()

(3)统计最大连续回答问题的天数大于等于3天的用户及其等级(若有多条符合条件的数据,按author_id升序排序)

  • [条件]:最大连续回答问题的天数大于等于3天

  • [使用]:group by 分组条件 [创作者;等级] having count(连续天数) >= 3 order by [创作者]

最终结果

select 查询结果 [创作者;等级;连续回答天数]
from 从哪张表中查询数据[多表]
group by 分组条件 [创作者;等级]
having 判断条件 [连续回答问题的天数大于等于3]
order by 对查询结果排序 [创作者升序];

求解代码

方法一:

with
    main as(
        #对日期、创作者进行去重
        select distinct
            answer_date,
            author_id
        from answer_tb
    )
    ,main1 as(
        #统计日期、创作者、日期减去排序的值(连续签到)
        select
            answer_date,
            author_id,
            answer_date - row_number() over(partition by author_id order by answer_date) as data
        from main
    )

#统计最大连续回答问题的天数大于等于3天的用户及其等级(若有多条符合条件的数据,按author_id升序排序)
select
    author_id,
    author_level,
    count(data) as days_cnt
from author_tb
join main1 using(author_id)
group by author_id,author_level having count(data) >= 3
order by author_id

方法二:

多表连接

#统计最大连续回答问题的天数大于等于3天的用户及其等级(若有多条符合条件的数据,按author_id升序排序)
select
    author_id,
    author_level,
    count(data) as days_cnt
from author_tb
join(
    #统计日期、创作者、日期减去排序的值(连续签到)
    select
        answer_date,
        author_id,
        answer_date - dense_rank() over(partition by author_id order by answer_date) as data
    from(
        #对日期、创作者进行去重
        select distinct
            answer_date,
            author_id
        from answer_tb
    ) main
) main1 using(author_id)
group by author_id,author_level having count(data) >= 3
order by author_id
发表于 2022-11-01 23:34:32 回复(0)
根据日期-dense_rank的差值相同进行分组
select b.author_id,a1.author_level,max(b.days_cnt)
from
    (select author_id,count(answer_date-rk) as days_cnt
    from
        (select distinct author_id,answer_date,
        dense_rank() over (partition by author_id order by answer_date) as rk
        from answer_tb) a
    group by author_id,answer_date-rk
    having days_cnt>=3) b
join author_tb a1
on b.author_id=a1.author_id
group by b.author_id,a1.author_level
order by author_id


发表于 2022-08-25 16:18:55 回复(0)
  • 本题目最大的难点就在于:如何判断几个日期是否连续的 —— 当一组日期按照升序排序时,如果某几个日期是连续的,它们减去本身的DENSE RANK,会发现都是相减的结果都是同一天
  • 不得不说,不知道是哪位先发现这个切入角度的的,真的是个人才。要是哪位知道这种数学规律的出处,求分享!
  • 另外,我发现大多数回答忽略了一个特殊情况(虽然题目实例中并没有考虑到):若一个用户有10次连续登录超过三天的情况,则该用户会得出10组“同一天”日期及其对应的连续天数(10个大于等于3的随机整数)
SELECT author_id, author_level, MAX(continous_days) AS days_cnt
FROM
    (SELECT author_id, 
           author_level, 
           COUNT(author_id) AS continous_days
     FROM
         (SELECT DISTINCT a1.author_id, a2.author_level, answer_date,
                 DENSE_RANK() OVER (PARTITION BY a1.author_id ORDER BY answer_date ASC) AS ranking
          FROM answer_tb AS a1 LEFT OUTER JOIN author_tb AS a2
          ON a1.author_id = a2.author_id) AS t1
     GROUP BY author_id, author_level, DATE_SUB(answer_date, INTERVAL ranking DAY)
     HAVING continous_days >= 3) AS t2
GROUP BY author_id, author_level
ORDER BY author_id ASC;
发表于 2022-08-12 22:34:30 回复(1)
知乎这一套题目都很简单啊。。。
主要是连续登录问题,用日期减去排序即可获得分组日期(不懂的可以去看看SQL12连续签到领金币 那题),再用group by去重即可
select
t2.author_id, t2.author_level, count(1) as days_cnt
from (
select n.author_id, a.author_level
, date_sub(n.answer_date, interval rank() over (partition by author_id order by answer_date) day) as group_date
from answer_tb n
left join author_tb a on a.author_id = n.author_id
group by n.answer_date, n.author_id, a.author_level
) t2
group by t2.author_id, t2.author_level,t2.group_date having days_cnt >= 3
order by t2.author_level desc


发表于 2022-04-30 16:01:43 回复(1)