首页 > 试题广场 >

每个月Top3的周杰伦歌曲

[编程题]每个月Top3的周杰伦歌曲
  • 热度指数:208922 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
从听歌流水中找到18-25岁用户在2022年每个月播放次数top 3的周杰伦的歌曲。

流水表 play_log:
日期 (fdate) 用户 ID (user_id) 歌曲 ID (song_id)
2022-01-08 10000 0
2022-01-16 10000 0
2022-01-20 10000 0
2022-01-25 10000 0
2022-01-02 10000 1
2022-01-12 10000 1
2022-01-13 10000 1
2022-01-14 10000 1
2022-01-10 10000 2
2022-01-11 10000 3
2022-01-16 10000 3
2022-01-11 10000 4
2022-01-27 10000 4
2022-02-05 10000 0
2022-02-19 10000 0
2022-02-07 10000 1
2022-02-27 10000 2
2022-02-25 10000 3
2022-02-03 10000 4
2022-02-16 10000 4
歌曲表song_info
歌曲 ID (song_id) 歌曲名称 (song_name) 歌手名称 (singer_name)
0 明明就 周杰伦
1 说好的幸福呢 周杰伦
2 江南 林俊杰
3 大笨钟 周杰伦
4 黑键 林俊杰
用户表user_info
user_id age
10000 18
输出:
month ranking song_name play_pv
1 1 明明就 4
1 2 说好的幸福呢 4
1 3 大笨钟 2
2 1 明明就 2
2 2 说好的幸福呢 1
2 3 大笨钟 1

示例1

输入

drop table if exists play_log;
create table `play_log` (
    `fdate` date,
    `user_id` int,
    `song_id` int
);
insert into play_log(fdate, user_id, song_id)
values 
('2022-01-08', 10000, 0),
('2022-01-16', 10000, 0),
('2022-01-20', 10000, 0),
('2022-01-25', 10000, 0),
('2022-01-02', 10000, 1),
('2022-01-12', 10000, 1),
('2022-01-13', 10000, 1),
('2022-01-14', 10000, 1),
('2022-01-10', 10000, 2),
('2022-01-11', 10000, 3),
('2022-01-16', 10000, 3),
('2022-01-11', 10000, 4),
('2022-01-27', 10000, 4),
('2022-02-05', 10000, 0),
('2022-02-19', 10000, 0),
('2022-02-07', 10000, 1),
('2022-02-27', 10000, 2),
('2022-02-25', 10000, 3),
('2022-02-03', 10000, 4),
('2022-02-16', 10000, 4);

drop table if exists song_info;
create table `song_info` (
    `song_id` int,
    `song_name` varchar(255),
    `singer_name` varchar(255)
);
insert into song_info(song_id, song_name, singer_name) 
values
(0, '明明就', '周杰伦'),
(1, '说好的幸福呢', '周杰伦'),
(2, '江南', '林俊杰'),
(3, '大笨钟', '周杰伦'),
(4, '黑键', '林俊杰');

drop table if exists user_info;
create table `user_info` (
    `user_id`   int,
    `age`       int
);
insert into user_info(user_id, age) 
values
(10000, 18)

输出

month|ranking|song_name|play_pv
1|1|明明就|4
1|2|说好的幸福呢|4
1|3|大笨钟|2
2|1|明明就|2
2|2|说好的幸福呢|1
2|3|大笨钟|1

说明

1月被18-25岁用户播放次数最高的三首歌为“明明就”、“说好的幸福呢”、“大笨钟”,“明明就”和“说好的幸福呢”播放次数相同,排名先后由两者的song_id先后顺序决定。2月同理。



备注:
MySQL中,日期转月份的函数为 month(),例:SELECT MONTH(‘2016-01-16') 返回 1。
难点一个窗口函数分组排序
row_number() over (
                partition by
                    month(fdate)
                order by
                    count(p.song_id) desc,
                    s.song_id
            ) as ranking

发表于 2025-12-26 15:05:07 回复(0)
select xx.month,	xx.ranking,	xx.song_name,	xx.play_pv
from (

select
    x.month,
    
    row_number() over (
        partition by
            x.month
        order by
            x.play_pv desc,
            x.song_id asc
    ) as ranking,
    x.song_name,
    x.play_pv
from
    (
        select
            month(a.fdate) as month,
            b.song_name,
            count(a.song_id) as play_pv,
            a.song_id
        from
            play_log a
            left join song_info b on a.song_id = b.song_id
            left join user_info c on a.user_id = c.user_id
        where
            b.singer_name = "周杰伦" and
            c.age >= 18 and c.age <= 25  and year(a.fdate) = 2022
        group by
            month,
            song_name,
            song_id
    ) x ) xx
where ranking <= 3

发表于 2025-11-27 02:15:20 回复(0)
SELECT c.month, row_number() over(partition by c.month order by c.play_pv desc) as ranking,c.song_name,c.play_pv
from
(select  substr(fdate,7,1) as month, song_name, song_id,count(*) as play_pv
from
(select a.*,b.age,c.song_name,c.singer_name  from play_log  a
left join user_info b on a.user_id=b.user_id
left join song_info c  on a.song_id=c.song_id  where singer_name='周杰伦' and substr(fdate,1,4)='2022' and age between 18 and 25) a group by substr(fdate,7,1),song_name,song_id
) c


这样写点击“”自测运行”的时候 是和结果输出一致的,但是点击“保存并提交”的时候有问题,是什么原因

发表于 2025-11-17 15:54:15 回复(0)
数量相同的时候要按照song_id升序排列,可能这就是考题和业务的差别
select
    *
from
    (
        select
            month(fdate) month,
            row_number() over (
                partition by
                    month(fdate)
                order by
                    count(*) desc,p.song_id asc
            ) ranking,
            song_name,
            count(*) play_pv
        from
            play_log p
            join song_info s on s.song_id = p.song_id
            and s.singer_name = '周杰伦'
            and year(p.fdate) = '2022'
            join user_info u on u.user_id = p.user_id
            and u.age between 18 and 25
            
        group by
            month,
            s.song_name,
            s.song_id
    ) count_info
where
    count_info.ranking <= 3


发表于 2025-11-16 11:14:13 回复(0)
如果提交报错,可能是因为存在排名听歌数量相同但是排名不一样的歌曲,因此需要在窗口函数的orderby部分按照song_id再升序排列即可.
发表于 2025-11-13 17:35:46 回复(0)
with t1 as (
select month(fdate) as month,x.user_id,song_id
from user_info x 
join play_log y 
using(user_id)
where year(fdate)=2022 and (age between 18 and 25)
),t2 as (
select t1.month,t1.song_id,song_name,count(song_id) as play_pv
from t1 
join song_info 
using(song_id)
where singer_name='周杰伦'
group by 1,2,3
)
select month,ranking,song_name,play_pv
from
(select month,song_id,song_name,play_pv,
row_number() over(partition by month order by play_pv desc,song_id) as ranking
from t2) e
where ranking<=3
order by 1,2

发表于 2025-10-16 11:22:19 回复(0)
with t as( select
            month(fdate) as month,
            song_id,
            count(song_id) as play_pv
        from
            play_log
            where user_id in
            (select
            user_id
            from user_info
            where age between 18 and 25)
        group by
            month(fdate),
            song_id)
select
    *
    from (select
        t.month, row_number() over (partition by t.month order by t.play_pv desc) as ranking,a.song_name,t.play_pv
from t join
(select
    *
    from song_info
    where singer_name="周杰伦") a
    on t.song_id=a.song_id) b
    where b.ranking<=3  
反思:有筛选字段在需要join的表里面,需要先join之后在进行row_number的排序
发表于 2025-10-06 20:19:07 回复(0)
窗口函数row_number()中要写t3.song_id asc,是因为在这个 SQL 查询中,row_number()函数里使用t3.song_id asc作为排序条件的一部分,主要是为了处理计数相同的情况,确保排序结果的唯一性和确定性。
select
    *
from(
    select 
        month(t1.fdate) as month
        ,row_number() over(partition by month(t1.fdate) order by count(1) desc,t3.song_id asc) as ranking
        ,t3.song_name
        ,count(1) as play_pv
    from play_log as t1
        left join user_info as t2 on t1.user_id = t2.user_id
        left join song_info as t3 on t1.song_id = t3.song_id
    where year(t1.fdate) = 2022
        and t2.age between 18 and 25
        and t3.singer_name = '周杰伦' 
    group by MONTH(t1.fdate),t1.song_id,t3.song_name
)t1 
where ranking <=3
order by month,ranking


发表于 2025-09-16 16:32:33 回复(0)
求助,代码通过自测运行但是提交没有通过,不知道哪里出了问题,求大神解答
select * from (select month,row_number()over(partition by month order by count(song_name) desc) as ranking,song_name, count(song_name) as play_pv  from (select year(fdate) as Y,month(fdate) as month,song_name,singer_name,age from play_log left join song_info on play_log.song_id=song_info.song_id left join user_info on play_log.user_id=user_info.user_id  where singer_name="周杰伦" and age between 18 and 25 ) as new where Y=2022  group by month,song_name ) as up where ranking<=3 order by month,ranking 
发表于 2025-09-11 16:14:45 回复(0)
小白不解 song_name和p.song_id不是一一对应的吗 为什么还要同时group by
发表于 2025-09-10 20:16:36 回复(2)
Select
    t.month
    ,t.ranking
    ,t.song_name
    ,t.play_pv
from
    (Select
        b.song_name
        ,month(a.fdate) as month
        ,count(*) as play_pv
        ,row_number () over (
            partition by month(a.fdate)
            order by count(*) desc, b.song_id asc
        ) as ranking
    from play_log a
    inner join user_info c on a.user_id = c.user_id
    inner join song_info b on a.song_id = b.song_id
    where b.singer_name = "周杰伦"
    and c.age between 18 and 25
    group by b.song_name, month(a.fdate),b.song_id
    )t
where t.ranking <= 3
order by t.month asc, t.ranking asc;
发表于 2025-09-01 21:20:57 回复(0)
select
    month,
    ranking,
    song_name,
    play_pv
from
    (
        select
            month(pl.fdate) as month,
            si.song_name,
            si.song_id,
            count(*) as play_pv,
            row_number() over (
                partition by
                    month(pl.fdate)
                order by
                    count(*) desc,si.song_id
            ) as ranking
        from
            play_log pl
            left join song_info si on pl.song_id = si.song_id
            left join user_info ui on pl.user_id = ui.user_id
        where
            si.singer_name = '周杰伦'
            and ui.age between 18 and 25
            and year(pl.fdate) = 2022
        group by month(pl.fdate),si.song_name,si.song_id
    ) as song_log
where
    ranking <= 3
order by
    month,ranking;

发表于 2025-08-17 00:50:56 回复(0)
row_number排序,数量相同的时候,序号就是随机的,这种答案还能判为错吗?

发表于 2025-08-08 10:50:38 回复(0)
select
    e.a 'month',
    e.b 'ranking',
    e.c 'song_name',
    e.d 'play_pv'
from
    (
        select
            extract(
                month
                from
                    play_log.fdate
            ) 'a',
            row_number() over (
                partition by
                    extract(
                        month
                        from
                            play_log.fdate
                    )
                ORDER BY
                    count(song_info.song_name) desc,song_info.song_id
            ) 'b',
            song_info.song_name 'c',
            count(*) 'd',
            song_info.song_id 'dc'
        from
            play_log
            inner join song_info on play_log.song_id = song_info.song_id
            inner join user_info on play_log.user_id = user_info.user_id
        where
            song_info.singer_name = '周杰伦'
            and user_info.age between 18 and 25
        group by
            extract(
                month
                from
                    play_log.fdate
            ),
            song_info.song_name,song_info.song_id
        order by
            extract(
                month
                from
                    play_log.fdate
            )
    ) e
where
    e.b < 4
    order by e.a,e.d desc,e.dc;
排序问题想了很久
发表于 2025-07-27 18:33:53 回复(0)
select
    *
from
    (
        select
            t.month,
            row_number() over (
                partition by
                    t.month
                order by
                    t.play_pv desc
            ) ranking,
            t.song_name,
            t.play_pv
        from
            (
                select
                    s.song_id,
                    MONTH(p.fdate) month,
                    s.song_name,
                    count(1) play_pv
                from
                    play_log p
                    inner join user_info u on u.user_id = p.user_id
                    inner join song_info s on s.song_id = p.song_id
                where
                    u.age >= 18
                    and u.age <= 25
                    and s.singer_name = '周杰伦'
                group by
                    month,
                    s.song_name,
                    s.song_id
                order by
                    month,
                    s.song_id
            ) t
    ) o
where
    o.ranking <= 3
先分组收集信息,再用窗口函数排行。
发表于 2025-07-24 23:36:27 回复(0)
1.先关联筛选数据,再聚合(聚合后一定要按song_id进行升序),再row_number按月份排序,最终筛选排序结果前3。
2.之前没按song_id排序,提交结果总有几个测试用例过不去(坑点)。
发表于 2025-07-18 18:02:42 回复(0)
求助 到底哪里不一样啊 怎么就执行结果不对呢 
select
    *
from (
    select
    month(p.fdate) as month,
    row_number() over( partition by  month(p.fdate)
    order by count(p.song_id) desc) as ranking,
    s.song_name,
    count(p.song_id) as play_pv
    from play_log p
    inner join song_info s
    on p.song_id = s.song_id
    inner join user_info us
    on p.user_id = us.user_id
    where us.age between 18 and 25
    and year(p.fdate) = 2022
    and s.singer_name = '周杰伦'
    group by month(p.fdate),s.song_name ) as a
where a.ranking <= 3
order by a.month,a.ranking

发表于 2025-06-30 00:11:26 回复(0)
group by 必须得加song_id 不然就不对 但我觉得加不加无所谓啊 又没要求
发表于 2025-06-16 20:48:39 回复(0)