首页 > 试题广场 >

牛客直播各科目同时在线人数

[编程题]牛客直播各科目同时在线人数
  • 热度指数:23927 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
牛客某页面推出了数据分析系列直播课程介绍。用户可以选择报名任意一场或多场直播课。
已知课程表course_tb如下(其中course_id代表课程编号,course_name表示课程名称,course_datetime代表上课时间):
上课情况表attend_tb如下(其中user_id表示用户编号、course_id代表课程编号、in_datetime表示进入直播间的时间、out_datetime表示离开直播间的时间):

请你统计每个科目最大同时在线人数(按course_id排序),以上数据的输出结果如下:

示例1

输入

drop table if exists course_tb;
CREATE TABLE course_tb(
course_id int(10) NOT NULL, 
course_name char(10) NOT NULL,
course_datetime char(30) NOT NULL);
INSERT INTO course_tb VALUES(1, 'Python', '2021-12-1 19:00-21:00');
INSERT INTO course_tb VALUES(2, 'SQL', '2021-12-2 19:00-21:00');
INSERT INTO course_tb VALUES(3, 'R', '2021-12-3 19:00-21:00');

drop table if exists attend_tb;
CREATE TABLE attend_tb(
user_id int(10) NOT NULL, 
course_id int(10) NOT NULL,
in_datetime datetime NOT NULL,
out_datetime datetime NOT NULL
);
INSERT INTO attend_tb VALUES(100, 1, '2021-12-1 19:00:00', '2021-12-1 19:28:00');
INSERT INTO attend_tb VALUES(100, 1, '2021-12-1 19:30:00', '2021-12-1 19:53:00');
INSERT INTO attend_tb VALUES(101, 1, '2021-12-1 19:00:00', '2021-12-1 20:55:00');
INSERT INTO attend_tb VALUES(102, 1, '2021-12-1 19:00:00', '2021-12-1 19:05:00');
INSERT INTO attend_tb VALUES(104, 1, '2021-12-1 19:00:00', '2021-12-1 20:59:00');
INSERT INTO attend_tb VALUES(101, 2, '2021-12-2 19:05:00', '2021-12-2 20:58:00');
INSERT INTO attend_tb VALUES(102, 2, '2021-12-2 18:55:00', '2021-12-2 21:00:00');
INSERT INTO attend_tb VALUES(104, 2, '2021-12-2 18:57:00', '2021-12-2 20:56:00');
INSERT INTO attend_tb VALUES(107, 2, '2021-12-2 19:10:00', '2021-12-2 19:18:00');
INSERT INTO attend_tb VALUES(100, 3, '2021-12-3 19:01:00', '2021-12-3 21:00:00');
INSERT INTO attend_tb VALUES(102, 3, '2021-12-3 18:58:00', '2021-12-3 19:05:00');
INSERT INTO attend_tb VALUES(108, 3, '2021-12-3 19:01:00', '2021-12-3 19:56:00');

输出

1|Python|4
2|SQL|4
3|R|3
with new_tab as (select user_id,course_id,in_datetime as time_,if(in_datetime is not null,1,0) as cnt
                from attend_tb
                union all
                select user_id,course_id,out_datetime as time_,if(out_datetime is not null,-1,0) as cnt
                from attend_tb)

select b.course_id,b.course_name,max(num)
from 
    (select *,sum(cnt)over(partition by course_id order by time_) as num
     from new_tab
    ) t 
 left join course_tb b on t.course_id=b.course_id
 group by b.course_id,b.course_name
 order by b.course_id

发表于 2022-04-10 21:05:39 回复(0)
select b.course_id,c.course_name,max(cnt) max_num
from
(select course_id,
sum(num) over (partition by course_id order by dt asc,num desc) cnt
from
(select course_id,in_datetime dt,1 num from attend_tb
union all 
select course_id,out_datetime dt,-1 num from attend_tb) as a) as b
inner join course_tb c on c.course_id=b.course_id
group by b.course_id,c.course_name
order by b.course_id
这道题目和某度信息流里面的SQL9的思路是一致的,虽然这道题中没有提到同一时间即有进来又有出去怎么处理,我就默认先计算进来的在计算出去的哈哈哈。
基本上,我认为这个方法是一个很好用的方法,可以用来求这类的问题
发表于 2021-12-05 08:31:14 回复(4)
最大同时在线人数问题
使用1,-1数据标签和sum over来解决
首先建立基础表以供查询
第一段 用户进入时间 设定值为1
使用union 并上
第二段 用户退出时间 设定值为-1
with temp as
(
select
course_id,
course_name,
user_id,
in_datetime as dt,
1 as flag
from attend_tb a
join course_tb b using(course_id)
union
select
course_id,
course_name,
user_id,
out_datetime as dt,
-1 as flag 
from attend_tb a
join course_tb b using(course_id)
)
然后我们使用sum函数 对1或-1值累计 按照时间顺序排序(重点)!
这样的话,可以有进入就加一,退出就减一,对表中的每一时刻进行了实时在线统计
select 
course_id,
course_name,
sum(flag) over (partition by course_id order by dt) as num
from temp 
在上表的基础上,按照course_id,course_name分组,找出最大值输出就可以了
最后别忘了按照course_id排序

完整代码如下
with temp as
(
select
course_id,
course_name,
user_id,
in_datetime as dt,
1 as flag
from attend_tb a
join course_tb b using(course_id)
union
select
course_id,
course_name,
user_id,
out_datetime as dt,
-1 as flag 
from attend_tb a
join course_tb b using(course_id)
)
select
course_id,
course_name,
max(num) as max_num
from
(
select 
course_id,
course_name,
sum(flag) over (partition by course_id order by dt) as num
from temp 
) t1
group by course_id,course_name
order by course_id


发表于 2022-05-18 23:42:24 回复(1)

【场景】:同时在线人数

【分类】:窗口函数、分组查询、多表连接

分析思路

难点:
1.窗口函数和分组查询要分开求解

(1)统计进出记录表

注:进入是增加一个在线人数,出去是减少一个在线人数

  • [条件]:timestampdiff(minute,in_datetime,out_datetime) >= 10

  • [使用]:union;使用1和-1标记进入、出去

(2)统计每个时刻的同时在线人数

  • [使用]:窗口函数 sum() over

(3)统计每个科目最大同时在线人数(按course_id排序)

  • [使用]:max() ; group by course_id,course_name; order by course_id

最终结果

select 查询结果 [课程号;课程名称;最大同时在线人数]
from 从哪张表中查询数据[多表]
group bu 分组条件 [课程号;课程名称]
order by 对查询结果排序 [按课程号升序排序];

求解代码

方法一

with子句

with
    main as(
        #进入是增加一个在线人数,出去是减少一个在线人数
        select
            user_id,
            course_id,
            in_datetime as datetime,
            1 as tag
        from attend_tb
        union
        select
            user_id,
            course_id,
            out_datetime as datetime,
            -1 as tag
        from attend_tb
    )
    ,main1 as(
        #统计每个时刻的同时在线人数
        select distinct
            course_id,
            course_name,
            sum(tag) over(partition by course_id order by datetime) as sum_num
        from course_tb
        left join main using(course_id)
    )
#统计每个科目最大同时在线人数(按course_id排序)
select
    course_id,
    course_name,
    max(sum_num) as max_num
from main1
group by course_id,course_name
order by course_id

方法二

多表连接

#统计每个科目最大同时在线人数(按course_id排序)
select
    course_id,
    course_name,
    max(sum_num) as max_num
from(
    select distinct
        course_id,
        course_name,
        sum(tag) over(partition by course_id order by datetime) as sum_num
    from course_tb
    left join(
        #进入是增加一个在线人数,出去是减少一个在线人数
        select
            user_id,
            course_id,
            in_datetime as datetime,
            1 as tag
        from attend_tb
        union
        select
            user_id,
            course_id,
            out_datetime as datetime,
            -1 as tag
        from attend_tb
    ) main using(course_id)
) main1
group by course_id,course_name
order by course_id
发表于 2022-10-30 19:02:23 回复(0)
select c.course_id, c.course_name, count(*) max_num 
from course_tb c 
left join attend_tb a on c.course_id = a.course_id
where a.in_datetime <= (select min(out_datetime) from attend_tb where course_id = c.course_id)
group by c.course_id, c.course_name
发表于 2021-12-24 11:14:01 回复(1)
select c,course_name,max(s)
from(select c,sum(r)over(partition by c order by t,r desc) s
    from(select course_id c,in_datetime t,1 r from attend_tb
        union all
        select course_id c,out_datetime t,-1 r from attend_tb)a)b
join course_tb b on c=b.course_id
group by 1,2
order by 1;

发表于 2024-08-04 19:29:51 回复(0)
with zb as (select ct.course_id,ct.course_name,in_datetime,out_datetime,1 as in_num,-1 as leave_num /*将进入时间标记为1,离开记-1*/
from course_tb ct
left join attend_tb at on ct.course_id=at.course_id
where date(course_datetime) = date(in_datetime)) /*建立一个要分割的表*/
select course_id,course_name,max(z_num) max_num
from
(select *,suM(in_num) over(partition by course_id order by in_datetime) z_num /*每科各时间段在线人数*/
from
(select course_id,course_name,in_datetime,in_num
from zb
union all /*上下连接,将时间连续,统计时间段在线人数*/
select course_id,course_name,out_datetime,leave_num
from zb) zb1) zb2
group by course_id,course_name
order by course_id

编辑于 2023-12-28 20:11:52 回复(0)
最大同时在线人数板子题。
我忽略了course_datetime这一列

with total_info as(
    select ct.course_id,ct.course_name,
    at.in_datetime as interval_start,
    at.out_datetime as interval_end
    from course_tb ct
    left join attend_tb at
    using(course_id)
)
,
# 准备前缀和
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再-m
    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 course_id,course_name,max(online_uv) as max_num from 
(
    select course_id,course_name,
    sum(cnt_change) over(partition by course_id order by tick,sort_util_flag) as online_uv
    from t2
) t3
group by course_id,course_name
order by course_id asc






发表于 2023-03-23 13:15:43 回复(0)
# 考点:同时在线人数的统计
#三步走

# 选择最大在线人数
SELECT
  C.course_id,D.course_name,max(total_num) as max_num
FROM
  (
    SELECT 
      # 窗口函数 sum() over() 计算在线人数
      user_id,course_id,pt,tag,
      sum(tag) over(partition by course_id order by pt,tag desc) as total_num
    FROM
      (
        #开始时间、离开时间打标签
          SELECT
          user_id,course_id,pt,tag
        FROM
          (
            SELECT
              user_id,course_id,in_datetime as pt,1 as tag
            FROM
              attend_tb
            union all
            SELECT
              user_id,course_id,out_datetime as pt,-1 as tag
            FROM
              attend_tb
          ) A
        order by pt asc
      ) B
  ) C
  left join course_tb D on C.course_id = D.course_id
group by
  C.course_id,D.course_name
order by
  C.course_id asc

发表于 2022-08-28 11:26:10 回复(0)
with attend_detail as  (
    select 
      course_id, active_time
    -- , cnt  -- 此种算法是同时计算进出,如果先算进或者先算出,可以保留 cnt 来单独计算
    -- 另:如果不需要同时计算进出,则不需要单独对 course_id, active_time 汇总,直接窗口函数
    , sum(cnt) as cnt 
    from (
        select course_id, in_datetime as active_time, 1 as cnt from attend_tb 
        union all
        select course_id, out_datetime as active_time, -1 as cnt from attend_tb 
    ) t
    group by course_id, active_time -- , cnt 
)

select 
  t2.course_id as course_id
, c.course_name as course_name 
, t2.max_num as max_num
from (
    select 
    course_id, max(online_num) as max_num
    from (
        select 
          course_id, active_time
        -- 此处计算同时进出,先计算进,则 order by active_time, cnt
        -- 先计算出,则 order by active_time, cnt desc
        -- 另外测试时间效率,窗口不如自关联
        , sum(cnt) over(partition by course_id order by active_time) as online_num
        from attend_detail 
    ) t1
    group by course_id
) t2
inner join course_tb c 
    on t2.course_id = c.course_id 
order by course_id 
;

发表于 2021-12-18 16:52:34 回复(0)
with t as (
    select user_id,course_id,in_datetime,1 as state
    from attend_tb
    union all 
    select user_id,course_id,out_datetime,-1 as state
    from attend_tb
)
select a.course_id,
       c.course_name,
       max(a.num) as max_num
from (
select course_id,
      sum(state) over (partition by course_id order by in_datetime,state desc) as num
      from t
    ) as a

join course_tb as c on a.course_id= c.course_id
group by 1,2
遇到好几次这种求最大在线人数的题,架轻就熟
发表于 2025-07-10 18:44:54 回复(0)
with temp as
(
select
course_id
,course_name
,in_datetime dt
,1 cnt
from attend_tb
left join course_tb
using(course_id)

union all

select
course_id
,course_name
,out_datetime
,-1
from attend_tb
left join course_tb
using(course_id)

order by 1,2,3,4 desc
)

select
course_id
,course_name
,max(asd)
from
(select
course_id
,course_name
,dt
,sum(cnt)over(partition by course_id,course_name order by dt,cnt desc) asd
from temp)a
group by 1,2
order by 1
发表于 2025-05-20 19:47:44 回复(0)
select
    course_id
    ,course_name
    ,max(num) as max_num
from
    (
        select
            a1.course_id
            ,course_name
            ,sum(cnt) over (order by dt) as num
        from
            (select user_id,course_id,in_datetime as dt,1 as cnt from attend_tb union all select user_id,course_id,out_datetime,-1 as cnt from attend_tb) a1
            inner join course_tb a2 using (course_id)
    ) t
group by
    course_id
    ,course_name
order by
    course_id
;

发表于 2025-04-23 16:16:24 回复(0)
with a as(
select course_id,course_name,in_datetime as time, 1 as tag
from attend_tb
left join course_tb using(course_id)
union all
select course_id,course_name,out_datetime as time, -1 as tag
from attend_tb
left join course_tb using(course_id))

select course_id,course_name,max(num) as max_num
from(
select course_id,course_name
,sum(tag)over(partition by course_id order by time,tag desc) as num
from a)b
group by course_id,course_name

发表于 2025-04-22 14:59:57 回复(0)
with temp as(
SELECT user_id, course_id, in_datetime as time, 1 as counter
FROM attend_tb
UNION ALL
SELECT user_id, course_id, out_datetime as time, -1 as counter
FROM attend_tb
ORDER BY time asc)
SELECT temp2.course_id, c.course_name, max(temp2.num_people) as max_num
FROM
(SELECT user_id, course_id, time,
SUM(counter) OVER(PARTITION BY course_id order by time asc) as num_people
FROM temp) as temp2
INNER JOIN course_tb c
ON temp2.course_id = c.course_id
GROUP BY temp2.course_id, c.course_name
ORDER BY temp2.course_id;

发表于 2025-04-20 09:41:49 回复(0)
这个套路题了哈哈哈
with a as
    (select course_id,max(so) max_num
    from  (select course_id,sum(num)over(partition by course_id
           order by cn ) so
           from (select course_id,in_datetime cn,1 num
                from attend_tb
                union all 
                select course_id,out_datetime cn,-1 num
                from attend_tb) t)t2
    group by course_id
    order by course_id )
select course_id,course_name,max_num
from a
left join course_tb using(course_id)

发表于 2025-04-17 15:52:45 回复(0)
和之前SQL 179 各城市最大同时等车人数基本同一个思路
#t1先获取各项信息,建立基础表
with t1 as
(
select
    b.course_id,
    course_name,
    course_datetime,
    user_id,
    in_datetime,
    out_datetime
from attend_tb as b
join course_tb as a
using(course_id)
),
#记录进入直播间的时间,人数+1
t2 as
(
select
    course_id,
    course_name,
    date_format(in_datetime,"%H:%i:%s") as dt,
    1 as tag
from t1
union all
#记录退出直播间的时间,人数-1
select
    course_id,
    course_name,
    if(date_format(out_datetime,"%H:%i:%s")<='21:00:00',date_format(out_datetime,"%H:%i:%s"),'21:00:00') as dt,
    -1 as tag
from t1
),
#按课程分组,按时间顺序对标识tag求和,得到每个课程每个时刻的在线人数
t3 as
(
select
    course_id,
    course_name,
    sum(tag) over(partition by course_id,course_name order by dt,tag desc) as num
from t2
)
#得到最大在线人数
select
    course_id,
    course_name,
    max(num) as max_num
from t3
group by course_id,course_name
order by course_id



发表于 2025-04-16 11:15:02 回复(0)
with t1 as (
    select c_tb.course_id as course_id,c_tb.course_name as course_name,
    a_tb.in_datetime as in_datetime,a_tb.out_datetime as out_datetime
    from
    attend_tb as a_tb left join course_tb as c_tb
    on a_tb.course_id = c_tb.course_id
),
t2 as (
    select course_id,course_name,in_datetime as dt,1 as is_in
    from t1
    union all
    select course_id,course_name,out_datetime as dt,-1 as is_in
    from t1
),
t3 as (
    select course_id,course_name,sum(is_in)  over(partition by course_id order by dt,is_in desc ) as cnt from t2
)
select course_id,course_name,max(cnt) from t3 group by 1,2 order by course_id
发表于 2025-04-10 14:44:34 回复(0)
with tmp as(
    select
    user_id,
    course_id,
    in_datetime as dt,
    1 as cnt
    from attend_tb
    union
    select
    user_id,
    course_id,
    out_datetime as dt,
    -1 as cnt
    from attend_tb
)

select
course_id,
course_name,
max(num) as max_num
from (
    select
    tmp.course_id,
    course_name,
    sum(cnt)over(partition by tmp.course_id order by dt,cnt) as num
    from tmp
    left join course_tb
    on tmp.course_id = course_tb.course_id
) a
group by 1,2
order by course_id

发表于 2025-03-20 00:00:06 回复(0)
本题四步走:
1.构建我们之后需要操作的基础表t1。
2.对用户进入时间dt(in_datetime)打一个标识 1 代表人数加一;
同时对用户离开时间dt(out_datetime) 打一个标识 -1 代表人数减一;两者union all连接,此时构成用户进入进出变化表t2。
3.按课程分组,按照日期顺序对标识求和,得到每类课程每个时刻在线人数表t3。
4.求最大值
with t1 as(
select course_tb.course_id course_id
,course_name
,in_datetime
,out_datetime
from course_tb
left join attend_tb
on course_tb.course_id=attend_tb.course_id
)
,t2 as(
select course_id,course_name,in_datetime dt,1 diff
from t1
union all
select course_id,course_name,out_datetime dt,-1 diff
from t1
)
,t3 as(
select course_id,course_name
,sum(diff)over(partition by course_id order by dt) num
from t2
)

select course_id,course_name
,max(num) max_num
from t3
group by course_id,course_name
order by 1;


发表于 2025-03-13 11:43:39 回复(1)