首页 > 试题广场 >

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

[编程题]某乎问答最大连续回答问题天数大于等于3天的用户及其对应等级
  • 热度指数:27905 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
现有某乎问答创作者信息表author_tb如下(其中author_id表示创作者编号、author_level表示创作者级别,共1-6六个级别、sex表示创作者性别):
author_id author_level sex
101 6 m
102 1 f
103 1 m
104 3 m
105 4 f
106 2 f
107 2 m
108 5 f
109 6 f
110 5 m
创作者回答情况表answer_tb如下(其中answer_date表示创作日期、author_id指创作者编号、issue_id指回答问题编号、char_len表示回答字数):
answer_date author_id issue_id char_len
2021-11-01 101 E001 150
2021-11-01
101 E002 200
2021-11-01
102 C003 50
2021-11-01
103 P001 35
2021-11-01
104 C003 120
2021-11-01
105 P001 125
2021-11-01
102 P002 105
2021-11-02
101 P001 201
2021-11-02
110 C002 200
2021-11-02
110 C001 225
2021-11-02
110 C002 220
2021-11-03
101 C002 180
2021-11-04
109 E003 130
2021-11-04
109 E001 123
2021-11-05
108 C001 160
2021-11-05
108 C002 120
2021-11-05
110 P001 180
2021-11-05
106 P002 45
2021-11-05
107 E003 56
请你统计最大连续回答问题的天数大于等于3天的用户及其等级(若有多条符合条件的数据,按author_id升序排序),以上例子的输出结果如下:
author_id author_level
days_cnt
101 6 3
示例1

输入

drop table if exists author_tb;
CREATE TABLE author_tb(
author_id int(10) NOT NULL, 
author_level int(10) NOT NULL,
sex char(10) NOT NULL);
INSERT INTO author_tb VALUES(101 , 6, 'm');
INSERT INTO author_tb VALUES(102 , 1, 'f');
INSERT INTO author_tb VALUES(103 , 1, 'm');
INSERT INTO author_tb VALUES(104 , 3, 'm');
INSERT INTO author_tb VALUES(105 , 4, 'f');
INSERT INTO author_tb VALUES(106 , 2, 'f');
INSERT INTO author_tb VALUES(107 , 2, 'm');
INSERT INTO author_tb VALUES(108 , 5, 'f');
INSERT INTO author_tb VALUES(109 , 6, 'f');
INSERT INTO author_tb VALUES(110 , 5, 'm');

drop table if exists answer_tb;
CREATE TABLE answer_tb(
answer_date date NOT NULL, 
author_id int(10) NOT NULL,
issue_id char(10) NOT NULL,
char_len int(10) NOT NULL);
INSERT INTO answer_tb VALUES('2021-11-1', 101, 'E001' ,150);
INSERT INTO answer_tb VALUES('2021-11-1', 101, 'E002', 200);
INSERT INTO answer_tb VALUES('2021-11-1',102, 'C003' ,50);
INSERT INTO answer_tb VALUES('2021-11-1' ,103, 'P001', 35);
INSERT INTO answer_tb VALUES('2021-11-1', 104, 'C003', 120);
INSERT INTO answer_tb VALUES('2021-11-1' ,105, 'P001', 125);
INSERT INTO answer_tb VALUES('2021-11-1' , 102, 'P002', 105);
INSERT INTO answer_tb VALUES('2021-11-2',  101, 'P001' ,201);
INSERT INTO answer_tb VALUES('2021-11-2',  110, 'C002', 200);
INSERT INTO answer_tb VALUES('2021-11-2',  110, 'C001', 225);
INSERT INTO answer_tb VALUES('2021-11-2' , 110, 'C002', 220);
INSERT INTO answer_tb VALUES('2021-11-3', 101, 'C002', 180);
INSERT INTO answer_tb VALUES('2021-11-4' ,109, 'E003', 130);
INSERT INTO answer_tb VALUES('2021-11-4', 109, 'E001',123);
INSERT INTO answer_tb VALUES('2021-11-5', 108, 'C001',160);
INSERT INTO answer_tb VALUES('2021-11-5', 108, 'C002', 120);
INSERT INTO answer_tb VALUES('2021-11-5', 110, 'P001', 180);
INSERT INTO answer_tb VALUES('2021-11-5' , 106, 'P002' , 45);
INSERT INTO answer_tb VALUES('2021-11-5' , 107, 'E003', 56);

输出

101|6|3
连续登陆天数问题,依然使用当前日期减去排序编号差值相等来解决
  • 首先,由于一个用户可能在同一天活跃多次,这样同一天的记录也有多次
所以在编号时使用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 回复(0)
# 实现原理
/*
连续登陆,也就是在连续登陆的期间内,后一天和前一天的差值为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)
这是我认为最严谨的写法
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 回复(0)
首先对每个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)
利用窗口函数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)
重点在于“最大连续回答问题天数大于等于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)
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 回复(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)
with tab as(
select *,ROW_NUMBER()over(partition by author_id order by rk3) as days
from(
    SELECT answer_date,author_id
           ,date_sub(answer_date,INTERVAL rank()over(order by answer_date,author_id) day) as rk3
#         ,rank()over(order by answer_date,author_id) as rk2
   from(
       select answer_date,author_id
       FROM answer_tb
       group by answer_date,author_id   #去重
        )ta
    )tb   #还原签到起始日期
)#起始签到日期相同计数(连续签到计数)

select author_id,author_level,max(days) as days_cnt
from author_tb as at1
     join tab using(author_id)
where days>=3
group by author_id,author_level

发表于 2022-02-13 16:21:32 回复(0)
with t1 as
(select *,dense_rank() over (partition by author_id order by answer_date asc) as days_cnt
from answer_tb)
select t1.author_id,t2.author_level,t1.days_cnt
from t1
left join author_tb t2 on t1.author_id = t2.author_id
where t1.days_cnt >=3
order by t1.author_id asc;
窗口函数后,应该再限制下取用户最大天数(窗口排名)那一条,不然如果连续日期超过3天,在这个写法下,一个用户会有多条。
发表于 2022-01-15 22:22:14 回复(1)
知乎的题目是整个系列最简单的
with t as(
select 
	answer_date,a1.author_id,author_level,
	date_sub(answer_date,interval DENSE_RANK() over(partition by a1.author_id order by answer_date) day ) same
from answer_tb a1
left join author_tb a2
on a1.author_id=a2.author_id )
select 
	author_id,
	author_level,
	count(distinct answer_date) days_cnt
from t 
group by 1,2,same
having count(distinct answer_date)>=3
order by 1


发表于 2022-01-11 08:42:51 回复(4)
  • 二维码

    扫描二维码,关注牛客网

  • 二维码

    下载牛客APP,随时随地刷题