首页 > 试题广场 >

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

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

用户行为日志表tb_user_log

id uid artical_id in_time out_time sign_in
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
SELECT artical_id, MAX(uv) AS max_uv
FROM(
    SELECT artical_id, time, SUM(flag) OVER(PARTITION BY artical_id ORDER BY time, flag DESC) AS uv
    FROM(
        SELECT artical_id, in_time AS time, 1 AS flag
        FROM tb_user_log
        WHERE artical_id != 0
        UNION ALL
        SELECT artical_id, out_time AS time, -1 AS flag
        FROM tb_user_log
        WHERE artical_id != 0
    )t
)t
GROUP BY artical_id
ORDER BY max_uv DESC;


发表于 2025-04-10 14:43:23 回复(0)
select
a.artical_id
,max(z) as max_uv
from
    (select
    a.artical_id
    ,a.in_time
    ,sum(if(a.out_time>=b.in_time and a.out_time<=b.out_time,1,0)) as z
    from tb_user_log as a
    join tb_user_log as b on a.artical_id=b.artical_id
    where a.artical_id<>"0"
    group by a.artical_id,a.in_time
    ) as a
group by a.artical_id
order by max_uv desc
很奇怪,为什么这道题if函数中我用in_time去标记1,0就是错的,用out_time去标记1,0就是对的?
用in_time时的表格聚合前都是对的

发表于 2025-03-22 11:42:17 回复(0)
SELECT artical_id,MAX(cnum) as max_uv
FROM(
SELECT
artical_id,
SUM(num)OVER(PARTITION BY artical_id ORDER BY dt,num DESC) AS cnum FROM(
SELECT
artical_id,
in_time as dt,
'1' AS num
FROM tb_user_log
UNION ALL
SELECT
artical_id,
out_time as dt,
'-1' AS num
FROM tb_user_log) AS sb1) AS sb2
GROUP BY artical_id
HAVING artical_id != 0
ORDER BY max_uv DESC;
为什么用having筛最后结果会保留三位小数呢。。。在mysql自测是没有小数位的
发表于 2024-11-29 16:30:32 回复(1)
用进入日期小于等于最小出去日期也能做
SELECT artical_id,count(*) as max_uv
FROM
(SELECT artical_id,in_time,out_time,MIN(out_time) over(PARTITION BY artical_id) as min_out_time
FROM
tb_user_log
WHERE artical_id != 0) as a
WHERE in_time <= min_out_time
GROUP BY artical_id
order by max_uv desc


发表于 2024-07-27 10:21:00 回复(0)
如果同一时刻有进入也有离开时,先记录用户数增加再记录减少,结果按最大人数降序。
因为题目有要求,所以在开窗中排序的话,需要按照时间升序以后,再按照正负一的字段降序。
发表于 2024-07-24 09:12:29 回复(0)
思路:
①将用户的进入时间和出去时间分开(分开才能同时赋值1/-1),进入的1,出去的-1(便于后续sum计算)。并且统一时间,都成为change_time,合并成一个时间轴;
②开窗对change_num求累计和,因为同一时间有进有出就先算进来的后算出去的,所以排序先按时间升序,再按计数降序。分组后的结果会按照书单分类,并在这个分组内按照时间change_time累积和;
③在每个分组里面求最大的累积和就是最多同时在线的人数
select artical_id, max(uv) as max_uv
from (
    select artical_id,
    sum(change_num) over(partition by artical_id order by change_time, change_num desc) as uv
    from
    (
        select artical_id, in_time as change_time, 1 as change_num
        from tb_user_log
        where artical_id !=0

        union all
        select artical_id, out_time as change_time, -1 as change_num
        from tb_user_log
        where artical_id !=0

    ) as a
) as b

group by artical_id
order by max_uv desc;


发表于 2024-07-13 17:20:55 回复(0)
with temp as(
    select 
    artical_id,
    in_time dt,
    1 tag
    from tb_user_log
    where artical_id != 0
    union all
    select 
    artical_id,
    out_time dt,
    -1 tag
    from tb_user_log
    where artical_id != 0  
)
select
artical_id,max(cnt) max_ux
from(
select 
artical_id,
sum(tag) over(partition by artical_id order by dt,tag desc) cnt
from temp
) t
group by artical_id
order by max_ux desc

发表于 2024-06-20 17:10:36 回复(0)
select
     artical_id
    ,max(uv) as max_uv
from (    
    select
         artical_id
        ,log_time
        ,sum(in_out_flag) over(partition by artical_id order by log_time, in_out_flag desc) as uv
    from(
        select
            uid
            ,artical_id
            ,in_time as log_time
            ,1    as in_out_flag
        from tb_user_log
        union all
        select
            uid
            ,artical_id
            ,out_time as log_time
            ,-1    as in_out_flag
        from tb_user_log
    ) as t1
    where artical_id != 0
) as tt1
group by
    artical_id
order by
    max_uv desc

发表于 2024-05-16 13:16:50 回复(0)
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)