首页 > 试题广场 >

每个月Top3的周杰伦歌曲

[编程题]每个月Top3的周杰伦歌曲
  • 热度指数:120017 时间限制: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。
小白求问:在哪里看有什么表啊
发表于 2024-09-10 10:47:02 回复(7)

这也报错我也是没话可说,要不就不要设置这种会有多种答案的情况,设置就应该考虑到多种情况的出现,还大厂真题,这水平也是没谁了
发表于 2025-02-12 14:53:44 回复(9)
with t as (
    select month(fdate) as `month`
        ,row_number() over (partition by month(fdate) order by count(*) desc,song_id) as ranking
        ,song_name
        ,count(*) as play_pv
    from play_log
    join user_info using(user_id)
    join song_info using(song_id)
    where year(fdate) = '2022'
    and age between 18 and 25
    and singer_name = '周杰伦'
    group by month(fdate),song_name,song_id    #因为窗口函数中用到song_id排序。所以分组时要加上song_id
)
select *
from t
where ranking <= 3

发表于 2024-08-09 11:19:08 回复(4)
从听歌流水中找到18-25岁用户在2022年每个月播放次数top 3的周杰伦的歌曲.
虽然这里只给出了一个用户的听歌记录,但是需要注意实际中可能会有多个不同年龄段用户.
STEP1: 左外连接play_log和user_info,再左外连接song_info表;
STEP2: 在此基础上筛选出2022年,18-25岁和歌手为周杰伦的所有听歌信息;
STEP3: 需要注意的是,需要新建一列RANKNG对听歌数进行排序,因此联想到使用窗口函数,并且由于存在相同排名但是只取3首歌的情况,所以使用ROW_NUMBER();
SELECT * 
FROM
    (SELECT MONTH(fdate) month,
    ROW_NUMBER() OVER(PARTITION BY MONTH(fdate) ORDER BY COUNT(s.song_id) DESC,p.song_id ASC) ranking,
    song_name,COUNT(s.song_id) play_pv
    FROM user_info u 
    LEFT JOIN play_log p ON u.user_id=p.user_id
    LEFT JOIN song_info s ON p.song_id=s.song_id
    WHERE YEAR(fdate)=2022 AND (age BETWEEN 18 AND 25) AND (singer_name='周杰伦')
    GROUP BY month,song_name,p.song_id) t1
WHERE ranking IN(1,2,3);
如果提交报错,可能是因为存在排名听歌数量相同但是排名不一样的歌曲,因此需要在窗口函数的orderby部分按照song_id再降序排列即可.
发表于 2024-08-31 11:18:27 回复(4)
题目也不说明相同play_pv, 按song_id升序
发表于 2025-01-18 17:52:03 回复(4)
select 
    *
from
(
# 按月份分区、按播放总量降序、歌曲id升序进行rank排序
select
    a.month,
    row_number() over(partition by a.month order by play_pv desc, song_id asc) as ranking,
    song_name,
    play_pv
from
(
# 先搜索周杰伦22年每个月的不同歌曲及播放总量
select
    month(pl.fdate) as month,
    si.song_name,
    count(si.song_name) as play_pv,
    si.song_id
from
    play_log as pl inner join
    song_info as si on si.song_id = pl.song_id inner join
    user_info as ui on ui.user_id = pl.user_id    
where
    ui.age >= 18 and 
    ui.age <= 25 and
    year(pl.fdate) = 2022 and 
    si.singer_name = "周杰伦"
group by 
    month(pl.fdate),
    si.song_name,
    si.song_id
) a
) b
having 
    ranking <= 3

发表于 2024-09-28 22:59:04 回复(0)
select *
from (
    select
        month(fdate) as month,
        row_number() over (partition by month(fdate) order by count(*) desc, play_log.song_id) as ranking,
        song_name,
        count(*) as play_pv
    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 18 <= age and age <= 25 and year(fdate) = '2022' and singer_name = '周杰伦'
    group by month(fdate), song_name, play_log.song_id
) as t
where ranking <= 3

发表于 2024-09-13 08:55:36 回复(1)
第一步:cte求出第一个表:月份,歌名,歌id,播放次数 (left join user_info区分出年纪,left join song_info得到歌名和歌id
                where里限制年龄,年份,和周杰伦。
第二步:cte1求出第二个表:月份,排名(播放次数作为评级的标准,如果播放次数相同则用歌id区分),歌名,播放次数。
第三步:选出前三的歌(我总觉得有办法把第二,三步合为一步,知道的请告知
                
with cte as(
select month(play_log.fdate) as month,
song_info.song_name,
song_info.song_id,
count(play_log.song_id)as play_pv
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 user_info.age between 18 and 25 and year(play_log.fdate)=2022
and song_info.singer_name='周杰伦'
group by month,song_info.song_id,song_info.song_name
)

,cte1 as (select month,rank() over (partition by month order by play_pv desc,song_id) as ranking,
song_name,play_pv
from cte)

select * from cte1 where ranking <4
发表于 2024-08-18 20:18:04 回复(1)
不使用窗口函数,应该怎么做?
我使用的MYSQL版本5.8  不支持ROW_NUMBER()
发表于 2025-01-11 15:13:19 回复(2)
with t as(
select month(pl.fdate) as month,
rank() over (partition by month(pl.fdate) order by count(pl.song_id) desc,si.song_id) as ranking,
si.song_name as song_name,
count(*) as play_pv

from play_log pl,song_info si,user_info ui
where pl.user_id=ui.user_id
and pl.song_id = si.song_id
and ui.age between 18 and 25 
and si.singer_name = '周杰伦'
and year(pl.fdate) ='2022'
group by month(pl.fdate),song_name,pl.song_id
)
select *
from t
where ranking <= 3

千幸万苦终于搞懂了

首先链接三张表,按照18-25,2022年,周杰伦的歌筛选条件,用rank函数对同一月份的总播放数排序,rank函数在排序时会有两个排序相等的情况,给他多设置一个参数可以解决这个问题,或者使用ROW_NUMBER()函数





发表于 2024-12-05 20:38:55 回复(3)
我写的好长。。。
WITH
    song_pv_month AS (
        SELECT
            MONTH (fdate) AS `month`,
            song_name,
            song_id,
            COUNT(*) AS play_pv
        FROM
            play_log
            LEFT JOIN user_info USING (user_id)
            LEFT JOIN song_info USING (song_id)
        WHERE
            age >= 18
            AND AGE <= 25
            AND YEAR (fdate) = 2022
            AND singer_name = '周杰伦'
        GROUP BY
            MONTH (fdate),
            song_name,
            song_id
    ),
    song_pv_month_rank AS (
        SELECT
            `month`,
            ROW_NUMBER() OVER (
                PARTITION BY
                    `month`
                ORDER BY
                    play_pv DESC,
                    song_id
            ) AS `ranking`,
            song_name,
            play_pv
        FROM
            song_pv_month
    )
SELECT
    `month`,
    ranking,
    song_name,
    play_pv
FROM
    song_pv_month_rank
WHERE
    ranking <= 3


发表于 2025-06-02 21:47:00 回复(0)
So *** you ambiguous answer.🖕🖕🖕🖕

发表于 2025-05-22 19:14:37 回复(0)
需要注意的是,题面的测试用例指出,
播放次数相同,排名先后由两者的song_id先后顺序决定

with t1 as(
    select p.*, s.song_name,s.singer_name,u.age,
    month(p.fdate) as `month`
    from play_log p 
    join song_info s 
    on p.song_id=s.song_id
    join user_info u
    on u.user_id=p.user_id
    where u.age between 18 and 25
    and s.singer_name="周杰伦"
)
,
tmp1 as 
(
    select `month`,song_name,song_id, count(*) as play_pv
    from t1
    group by `month`,song_name,song_id
)

# select * from tmp1
,
t2 as (
select `month`, row_number() over(partition by `month` order by play_pv desc, song_id asc) as ranking, song_name,play_pv
from tmp1)

select * from t2
where ranking<=3
order by `month` asc, ranking asc




发表于 2025-04-15 14:57:05 回复(0)
同play_pv, 按song_id升序
发表于 2025-03-23 21:10:31 回复(0)
如果某个月里,有两首或多首歌的播放量是一样的,那么还要按照歌曲编号再排一次顺序。
确实,题目没有明确说明这个要求,但实际项目中要考虑的东西甚至不止于此!求职者能否考虑到这些隐藏条件,也是面试官要考察的一部分!
            ROW_NUMBER() OVER (
                PARTITION BY
                    MONTH (fdate)
                ORDER BY
                    COUNT(1) DESC,
                    song_info.song_id ASC
            ) AS ranking
发表于 2025-02-14 11:39:06 回复(0)
with t1 as(
select
    month(p.fdate) month
    ,p.song_id song_id
    ,count(p.song_id) play_pv
from
    play_log p
join
    user_info u on p.user_id = u.user_id
where
    year(p.fdate)=2022  and u.age>=18 and u.age<=25
group by
    month(p.fdate),p.song_id),
t2 as(
select
    t1.month month
    ,row_number()over(partition by t1.month order by t1.play_pv desc,t1.song_id) ranking
    ,s.song_name song_name
    ,t1.play_pv play_pv
from
    t1
join
    song_info s
on
    t1.song_id = s.song_id
where s.singer_name = '周杰伦')

select month,ranking,song_name,play_pv from t2 where ranking in (1,2,3)
发表于 2024-10-25 15:55:40 回复(0)
group by 必须得加song_id 不然就不对 但我觉得加不加无所谓啊 又没要求
发表于 2025-06-16 20:48:39 回复(0)
select month,ranking,song_name,play_pv from
(select month(fdate) month, song_name,count(*) play_pv, rank() over(partition by month(fdate) order by count(*) desc,song_id) ranking
 from
(select fdate,song_name,song_id
from play_log
join song_info using(song_id)
join user_info using(user_id)
where year(fdate)='2022' and singer_name='周杰伦' and age between 18 and 25) t
group by month,song_name,song_id) age
where ranking<=3
发表于 2025-06-10 21:03:18 回复(0)
SELECT month,ranking,song_name,play_pv
FROM (
    SELECT 
        MONTH(p.fdate) AS month,
        ROW_NUMBER() OVER (PARTITION BY MONTH(p.fdate) ORDER BY COUNT(*) DESC,
s.song_id ASC) AS ranking,#partition必须有,否则不分组排序,全部排序。id必须有,排序结果不一致(实际不必要)
        s.song_name,
        COUNT(*) AS play_pv#每个组内的总行数
    FROM 
        play_log p#中间表
    JOIN 
        song_info s ON s.song_id = p.song_id
    JOIN 
        user_info u ON u.user_id = p.user_id
    WHERE 
        s.singer_name = '周杰伦'
        AND u.age BETWEEN 18 AND 25
        AND YEAR(p.fdate) = 2022
    GROUP BY 
        MONTH(p.fdate), s.song_name, s.song_id#按顺序分组,name为凑数
) AS sub#注意
WHERE 
    sub.ranking <= 3
发表于 2025-06-10 11:19:40 回复(0)
desc play_log;
SELECT * FROM play_log;
ALTER TABLE play_log CHANGE `日期 (fdate)` shijian date;
ALTER TABLE play_log CHANGE `歌曲 ID (song_id)` song_id int;
ALTER TABLE play_log CHANGE `用户 ID (user_id)` user_id int;
SELECT * FROM play_log;
WITH t2 as (
SELECT DATE_FORMAT(play_log.shijian,'%Y-%m')  AS yuefen ,song_info.`歌曲名称 (song_name)`,song_info.`歌曲 ID (song_id)`, count(1) as num ,  ROW_NUMBER() OVER (PARTITION BY DATE_FORMAT(play_log.shijian, '%Y-%m') ORDER BY COUNT(1) DESC,song_info.`歌曲 ID (song_id)`) AS rk
FROM play_log JOIN song_info ON play_log.song_id = song_info.`歌曲 ID (song_id)`
GROUP BY yuefen,`歌曲名称 (song_name)`,`歌曲 ID (song_id)`),
t3 as (
SELECT yuefen,`歌曲名称 (song_name)`,`歌曲 ID (song_id)`,num, rk FROM t2 
where rk <= 3)
SELECT * FROM t3 ;
发表于 2025-06-08 23:17:28 回复(0)