首页 > 试题广场 >

每篇文章同一时刻最大在看人数

[编程题]每篇文章同一时刻最大在看人数
  • 热度指数:75724 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

用户行为日志表tb_user_log

id uid artical_id in_time out_time sign_cin
1 101 9001 2021-11-01 10:00:00 2021-11-01 10:00:11 0
2 102 9001
2021-11-01 10:00:09 2021-11-01 10:00:38 0
3 103 9001
2021-11-01 10:00:28 2021-11-01 10:00:58 0
4 104 9002 2021-11-01 11:00:45 2021-11-01 11:01:11 0
5 105 9001
2021-11-01 10:00:51 2021-11-01 10:00:59
0
6
106 9002 2021-11-01 11:00:55
2021-11-01 11:01:24
0
7 107 9001 2021-11-01 10:00:01
2021-11-01 10:01:50
0
(uid-用户ID, artical_id-文章ID, in_time-进入时间, out_time-离开时间, sign_in-是否签到)


场景逻辑说明artical_id-文章ID代表用户浏览的文章的ID,artical_id-文章ID0表示用户在非文章内容页(比如App内的列表页、活动页等)。

问题:统计每篇文章同一时刻最大在看人数,如果同一时刻有进入也有离开时,先记录用户数增加再记录减少,结果按最大人数降序。

输出示例
示例数据的输出结果如下

artical_id max_uv
9001 3
9002 2
解释:10点0分10秒时,有3个用户正在浏览文章9001;11点01分0秒时,有2个用户正在浏览文章9002。

示例1

输入

DROP TABLE IF EXISTS tb_user_log;
CREATE TABLE tb_user_log (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '自增ID',
    uid INT NOT NULL COMMENT '用户ID',
    artical_id INT NOT NULL COMMENT '视频ID',
    in_time datetime COMMENT '进入时间',
    out_time datetime COMMENT '离开时间',
    sign_in TINYINT DEFAULT 0 COMMENT '是否签到'
) CHARACTER SET utf8 COLLATE utf8_bin;

INSERT INTO tb_user_log(uid, artical_id, in_time, out_time, sign_in) VALUES
  (101, 9001, '2021-11-01 10:00:00', '2021-11-01 10:00:11', 0),
  (102, 9001, '2021-11-01 10:00:09', '2021-11-01 10:00:38', 0),
  (103, 9001, '2021-11-01 10:00:28', '2021-11-01 10:00:58', 0),
  (104, 9002, '2021-11-01 11:00:45', '2021-11-01 11:01:11', 0),
  (105, 9001, '2021-11-01 10:00:51', '2021-11-01 10:00:59', 0),
  (106, 9002, '2021-11-01 11:00:55', '2021-11-01 11:01:24', 0),
  (107, 9001, '2021-11-01 10:00:01', '2021-11-01 10:01:50', 0);

输出

9001|3
9002|2
with a as (
select uid,artical_id,in_time as dt,1 as flag from tb_user_log where artical_id!=0
union all
select uid,artical_id,out_time as dt,-1 as flag from tb_user_log where artical_id!=0),
b as (
select artical_id,
sum(flag) over (partition by artical_id order by dt,flag desc) as uv
from a)

select artical_id,max(uv) as max_uv
from b 
group by artical_id 
order by max_uv desc

编辑于 2024-04-11 10:07:22 回复(0)
第一种方法,使用笛卡尔积,面向集合,思路是at作为每个时间点与每个时间段it-ot比较,at在it-ot之间就留下
with 
t as (
select t.artical_id as aid, uid,
    t.in_time as it, 
    t.out_time as ot,
    tt.in_time as at
from tb_user_log as t
    cross join (select artical_id, in_time from tb_user_log)tt using(artical_id)
),
t1 as (
select aid, at, count(distinct uid) as maxn
from t 
where it<=at and ot>=at
group by aid, at
)

select aid, max(maxn) from t1
where aid != 0
group by aid
order by max(maxn) desc;
第二种方法,使用窗口函数,面向过程,思路就是给每个进入和退出时间点赋值1和-1,按照时间排序,然后累加1和-1
with 
t as (
select artical_id, in_time as time, 1 as tag
from tb_user_log 
union all 
select artical_id, out_time as time, -1 as tag
from tb_user_log
)

select artical_id, max(max_uv) 
from
    (select 
    artical_id
    , sum(tag) over(partition by artical_id order by time, tag desc) as max_uv
    from t)t
where artical_id <> 0
group by artical_id
order by  max(max_uv) desc;


编辑于 2024-02-13 21:07:36 回复(1)
with r1 as (
    select artical_id,in_time as time,1 as t0
from tb_user_log
where artical_id !='0'
union all
select artical_id,  out_time as time,-1 as t0
from tb_user_log
where artical_id !='0'
order by time,t0
)
select artical_id,max(uv) as max_uv
from (
    select artical_id,sum(t0)over(partition by artical_id order by time,t0 desc) as uv
from r1
)r2
group by artical_id
order by max_uv desc
发表于 2023-10-06 21:26:28 回复(0)
select artical_id,max(num) as max_uv
from (select artical_id,sum(td) over(partition by artical_id order by dt, td desc ) as num
from(
select artical_id,uid,in_time as dt, 1 as td
from tb_user_log
where artical_id >0
union all
select artical_id,uid,out_time as dt, -1 as td
from tb_user_log
where artical_id>0) bb )aa
group by artical_id
order by max_uv desc
感觉套路都是一样的

发表于 2023-09-08 21:44:57 回复(0)
-- intime和outtime列转行合并,有了完整的时间线,每一行只对应一个操作
with t1 as(select uid,artical_id,in_time timeline,1 addnum from tb_user_log where artical_id != 0 
             union
            select uid,artical_id,out_time timeline,-1 addnum from tb_user_log where artical_id != 0 ),
t2 as(select * from t1 order by timeline), -- 按时间线顺序整理表格
t3 as(select *,sum(addnum) over(partition by artical_id order by timeline asc, addnum DESC) uv from t2) -- 累记每一时刻行的用户数,先记录用户数增加再记录减少,所以嵌套order来sum
select artical_id,max(uv) max_uv from t3 -- 找到每个文章累积最多人的那一行
group by artical_id
order by max_uv DESC

发表于 2023-08-05 15:20:26 回复(0)
尝试不用赋予编码的解法 

select artical_id,max(online)
from (select  * ,
       (select count(if(artical_id=0,null,uid)) 
        from  tb_user_log_21 as tu1
        where tu1.artical_id=tu2.artical_id 
                and tu1.in_time <= tu2.in_time 
                and tu2.in_time <= tu1.out_time ) as online
from  tb_user_log_21 as tu2) as a
where  artical_id !=0
group by artical_id;
发表于 2023-07-20 21:42:30 回复(0)
select
    artical_id,
    max(uv) as max_uv
from
    (
        select
            artical_id,
            time,
            sum(uv) as uv
        from
            (
                select
                    artical_id,
                    in_time,
                    out_time,
                    time,
                    case
                        when time >= in_time
                        and time <= out_time then 1
                        else 0
                    end as "uv"
                from
                    (
                        select
                            artical_id,
                            in_time,
                            out_time
                        from
                            tb_user_log
                    ) t1
                    cross join (
                        select distinct
                            in_time as time
                        from
                            tb_user_log
                    ) t2
                where
                    artical_id != 0
            ) A
        group by
            artical_id,
            time
    ) B
group by
    artical_id
order by
    max_uv desc;
发表于 2023-06-14 20:02:05 回复(0)
SELECT
    artical_id,
    MAX(total_user) AS max_uv
FROM(
SELECT
    artical_id,
    SUM(diff) OVER(PARTITION BY artical_id ORDER BY time,diff DESC) AS total_user
FROM(
SELECT
    uid,
    artical_id,
    in_time AS time,
    1 AS diff
FROM tb_user_log
UNION
SELECT
    uid,
    artical_id,
    out_time AS time,
    -1 AS diff
FROM tb_user_log
) a
) b
WHERE artical_id != 0
GROUP BY 1
ORDER BY 2 DESC

发表于 2023-06-02 17:45:39 回复(0)
select artical_id, 
max(view_count) as max_uv

from
(select artical_id, 
sum(io)over(partition by artical_id order by time asc, io desc) as view_count

from 

(select uid, 
artical_id,
in_time as time,
1 as io
from tb_user_log
where artical_id != 0 

union 

select uid, 
artical_id,
out_time as time,
-1 as io
from tb_user_log
where artical_id != 0 ) as t1 
) as t2 

group by artical_id
order by max_uv desc

发表于 2023-05-05 16:25:24 回复(0)
select
    artical_id,
    max(num) as max_uv
from
    (
        select
            t1.artical_id,
            sum(
                if (
                    t1.out_time >= t2.in_time
                    and t1.out_time <= t2.out_time,
                    1,
                    0
                )
            ) as num
        from
            tb_user_log t1
            left join tb_user_log t2 on t1.artical_id = t2.artical_id
        where
            t1.artical_id != 0
        group by
            t1.artical_id,
            t1.id
    ) as t
group by
    artical_id
order by
    max_uv desc

发表于 2023-04-20 21:07:24 回复(0)
注意一点:
如果同一时刻有进入也有离开时,先记录用户数增加再记录减少
所以
sum(score) over(partition by artical_id order by time_time asc,score desc)



-- 思路:
-- (1)将9002有一个人在11:00:45进入,在11:01:11离开拆成 9002 11:00:45 +1 和9002 11:01:11 -1
-- (2)按照时间升序,sum后面的number,最大值即为t最大在看人数

select
    artical_id,
    max(uv) as max_uv
from
(
    select
        artical_id,
        time_time,
        -- 如果同一时刻有进入也有离开时,先记录用户数增加再记录减少
        sum(score) over(partition by artical_id order by time_time asc,score desc) as uv
    from
    (
        select 
            artical_id,
            in_time as time_time,
            1 as score
        from tb_user_log
        where artical_id != 0
        union all
        select 
            artical_id,
            out_time as time_time,
            -1 as score
        from tb_user_log
        where artical_id != 0
    ) tmp
) tmp1
group by artical_id
-- 结果按最大人数降序
order by max(uv) desc



发表于 2023-03-27 23:02:37 回复(0)
最大同时在线人数板子题
with total_info as(
    select artical_id,
    in_time as interval_start,
    out_time as interval_end
    from tb_user_log
    where artical_id<>0
)
,
# 准备前缀和
t2 as(
    select *,
    (case when tick=interval_start then +1 when tick=interval_end then -1 else 0 end) as cnt_change,
    (case when interval_start=tick then 1 when interval_end=tick then 2 else 0 end) as sort_util_flag  # 如果某个时刻,有n人进入,有m人出去,同时在线人数的变动为:n个+1,之后m个-1
    # (case when interval_start=tick then 2 when interval_end=tick then 1 else 0 end) as sort_util_flag  # 如果某个时刻,有n人进入,有m人出去,同时在线人数的变动为:m个-1,之后n个+1
    from
    (
        select interval_start as tick from total_info
        union 
        select interval_end as tick from total_info
    )tmp1 
    left join total_info
    on interval_start=tick&nbs***bsp;interval_end=tick
) 

select artical_id,max(online_uv) as max_num from 
(
    select artical_id,
    sum(cnt_change) over(partition by artical_id order by tick,sort_util_flag) as online_uv
    from t2
) t3
group by artical_id
order by max_num desc



发表于 2023-03-23 13:27:39 回复(0)
请问为什么在拼接的时候,使用union无法通过第三个测试用例,而换成union all之后就可以了呢?
with t as (
    select artical_id, in_time as times, 1 as diff
    from tb_user_log
    union all
    select artical_id, out_time as times, -1 as diff
    from tb_user_log
)

select artical_id, max(uv) as max_uv
from (
    select artical_id, times, sum(diff) over(partition by artical_id order by times, diff desc) as uv
    from t 
    where artical_id != 0
) t2
group by artical_id
order by max_uv desc
按道理来说这里有新添加的1 -1列,不可能有重复的记录被删除了啊

发表于 2023-03-12 18:14:44 回复(0)
SELECT artical_id,MAX(MU) max_nu
FROM
(
    SELECT artical_id,in_time,SUM(num)OVER(PARTITION BY artical_id ORDER BY in_time,num DESC ) MU
    FROM    
    (
        SELECT artical_id,in_time,1 num
        FROM tb_user_log
        WHERE artical_id<>0
        UNION ALL
        SELECT artical_id,out_time,-1 num
        FROM tb_user_log
        WHERE artical_id<>0
    )T
)T
GROUP BY artical_id
ORDER BY max_nu DESC

发表于 2023-02-21 01:12:59 回复(0)
select artical_id,max(total) as max_uv 
from (
    select artical_id,
    sum(flag) over(partition by artical_id order by dt,flag desc) total
    from (
       select artical_id,uid,in_time as dt,1 as flag
       from tb_user_log
       union all
       select artical_id,uid,out_time as dt,-1 as flag
       from tb_user_log
    )t1
)t2
where artical_id!=0
group by 1
order by 2 desc

发表于 2023-01-20 16:40:34 回复(0)
又又又又学到了:
(1)计算实时uv,按进入时间、离开时间的时间维度升序排序,进入记为uv=1,离开记为uv = -1,则通过窗口函数sum() over(partition by col order by time)可计算出实时在线人数;
(2)本题坑点:①同一时刻需要先计流入再计流出,故需将流入的uv排序在前;②可能有(其实是测试用例有)同一时刻不同用户流入,并同时在相同时刻流出,此时union会去重,注意使用unionall或者union时添加用户字段;
代码如下:
with tb as (
select 
    t.artical_id,
    t.time,
    sum(t.uv) over(partition by t.artical_id order by t.time,t.uv desc) as sum_uv
from 
(
    select uid,artical_id,in_time as time,1 as uv from tb_user_log
union
    select uid,artical_id,out_time as time,-1 as uv from tb_user_log 
) as t
where t.artical_id != 0 )
select tb.artical_id,max(tb.sum_uv) as max_uv from tb 
group by tb.artical_id
order by max_uv desc


发表于 2023-01-01 02:54:39 回复(1)
select artical_id,max(num) as max_uv
from(
    select artical_id,
    sum(flag) over(partition by artical_id order by dt,flag desc) as num
    from(
        select artical_id,in_time dt,1 as flag from tb_user_log
        where artical_id != 0
        union all
        select artical_id,out_time dt,-1 as flag from tb_user_log
       where artical_id != 0
    )t1 
)t2
group by artical_id
order by max_uv desc;


发表于 2022-11-21 10:21:51 回复(0)
  • 二维码

    扫描二维码,关注牛客网

  • 二维码

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