楼主: Myron2017
跳转到指定楼层
上一主题 下一主题
收起左侧

刷题记录帖子

🔗
 楼主| Myron2017 7 小时前 | 只看该作者
全局:
1917. Leetcodify Friends Recommendations
Hard
Topics
SQL Schema
Pandas Schema
Table: Listens

+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| user_id     | int     |
| song_id     | int     |
| day         | date    |
+-------------+---------+
This table may contain duplicates (In other words, there is no primary key for this table in SQL).
Each row of this table indicates that the user user_id listened to the song song_id on the day day.


Table: Friendship

+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| user1_id      | int     |
| user2_id      | int     |
+---------------+---------+
In SQL,(user1_id, user2_id) is the primary key for this table.
Each row of this table indicates that the users user1_id and user2_id are friends.
Note that user1_id < user2_id.


Recommend friends to Leetcodify users. We recommend user x to user y if:

Users x and y are not friends, and
Users x and y listened to the same three or more different songs on the same day.
Note that friend recommendations are unidirectional, meaning if user x and user y should be recommended to each other, the result table should have both user x recommended to user y and user y recommended to user x. Also, note that the result table should not contain duplicates (i.e., user y should not be recommended to user x multiple times.).

Return the result table in any order.

The result format is in the following example.



Example 1:

Input:
Listens table:
+---------+---------+------------+
| user_id | song_id | day        |
+---------+---------+------------+
| 1       | 10      | 2021-03-15 |
| 1       | 11      | 2021-03-15 |
| 1       | 12      | 2021-03-15 |
| 2       | 10      | 2021-03-15 |
| 2       | 11      | 2021-03-15 |
| 2       | 12      | 2021-03-15 |
| 3       | 10      | 2021-03-15 |
| 3       | 11      | 2021-03-15 |
| 3       | 12      | 2021-03-15 |
| 4       | 10      | 2021-03-15 |
| 4       | 11      | 2021-03-15 |
| 4       | 13      | 2021-03-15 |
| 5       | 10      | 2021-03-16 |
| 5       | 11      | 2021-03-16 |
| 5       | 12      | 2021-03-16 |
+---------+---------+------------+
Friendship table:
+----------+----------+
| user1_id | user2_id |
+----------+----------+
| 1        | 2        |
+----------+----------+
Output:
+---------+----------------+
| user_id | recommended_id |
+---------+----------------+
| 1       | 3              |
| 2       | 3              |
| 3       | 1              |
| 3       | 2              |
+---------+----------------+
Explanation:
Users 1 and 2 listened to songs 10, 11, and 12 on the same day, but they are already friends.
Users 1 and 3 listened to songs 10, 11, and 12 on the same day. Since they are not friends, we recommend them to each other.
Users 1 and 4 did not listen to the same three songs.
Users 1 and 5 listened to songs 10, 11, and 12, but on different days.

Similarly, we can see that users 2 and 3 listened to songs 10, 11, and 12 on the same day and are not friends, so we recommend them to each other.
  1. # Write your MySQL query statement below
  2. WITH L AS (
  3.     SELECT DISTINCT user_id, song_id, day
  4.     FROM Listens
  5. ),
  6. Pairs AS (
  7.     SELECT
  8.         a.user_id AS user_id,
  9.         b.user_id AS recommended_id
  10.     FROM L a
  11.     JOIN L b
  12.       ON a.day = b.day
  13.      AND a.song_id = b.song_id
  14.      AND a.user_id <> b.user_id
  15.     GROUP BY a.user_id, b.user_id, a.day
  16.     HAVING COUNT(*) >= 3
  17. )
  18. SELECT DISTINCT p.user_id, p.recommended_id
  19. FROM Pairs p
  20. WHERE NOT EXISTS (
  21.     SELECT 1
  22.     FROM Friendship f
  23.     WHERE f.user1_id = LEAST(p.user_id, p.recommended_id)
  24.       AND f.user2_id = GREATEST(p.user_id, p.recommended_id)
  25. );
复制代码
题目大意

给用户 y 推荐用户 x,需要同时满足两个条件:

两人不是好友。
两人在同一天听过至少 3 首相同的不同歌曲。

推荐是单向的:如果 x 和 y 互相符合条件,结果里要有 (x→y) 和 (y→x) 两行,同一对不能重复出现。

解法思路
第一步:去重(CTE L)
sql
SELECT DISTINCT user_id, song_id, day FROM Listens

Listens 没有主键,可能有重复行(同一用户同一天重复听同一首歌)。题目要求的是 3 首不同的歌,所以先去重,之后 COUNT(*) 就等于共同歌曲的数量。

第二步:自连接找出"同一天听同一首歌"的用户对(CTE Pairs)
sql
FROM L a
JOIN L b
  ON a.day = b.day
AND a.song_id = b.song_id
AND a.user_id <> b.user_id
a.day = b.day 且 a.song_id = b.song_id:两人同一天听了同一首歌。
a.user_id <> b.user_id:排除用户和自己配对。
自连接会同时产生 (1,3) 和 (3,1),正好满足"双向推荐"的要求,不用额外处理。
第三步:按天分组,统计共同歌曲数
sql
GROUP BY a.user_id, b.user_id, a.day
HAVING COUNT(*) >= 3

分组里必须带上 a.day,这是这道题最容易错的地方:

带 day:要求 3 首歌在同一天听的,符合题意。
不带 day:会把不同日期的歌加在一起,比如周一 2 首加周二 1 首,错误地算作 3 首。

示例中用户 1 和用户 5 听的歌相同,但日期不同,所以不会被推荐。

第四步:排除已经是好友的用户对
sql
WHERE NOT EXISTS (
    SELECT 1 FROM Friendship f
    WHERE f.user1_id = LEAST(p.user_id, p.recommended_id)
      AND f.user2_id = GREATEST(p.user_id, p.recommended_id)
)

Friendship 表保证 user1_id < user2_id,每对好友只存一行。而 Pairs 里两个方向都有,所以先用 LEAST / GREATEST 把用户对规范化成"小的在前,大的在后",再去 Friendship 里查是否存在。

第五步:最外层的 DISTINCT

同一对用户可能在多个不同的日子都满足 3 首歌的条件,会在 Pairs 里出现多行。题目要求结果不能有重复,所以需要 DISTINCT。

用示例走一遍
用户对        共同歌曲情况        结果
1 和 2        3 月 15 日听了 10、11、12        满足条件,但已是好友,排除
1 和 3        3 月 15 日听了 10、11、12        保留,输出 (1,3) 和 (3,1)
2 和 3        同上        保留,输出 (2,3) 和 (3,2)
1 和 4        只共同听了 10、11,共 2 首        不够 3 首
1 和 5        听的歌相同,但日期不同        不在同一天,不满足

最终输出 (1,3)、(2,3)、(3,1)、(3,2),与预期一致。
回复

使用道具 举报

🔗
 楼主| Myron2017 6 小时前 | 只看该作者
全局:
1919. Leetcodify Similar Friends
Hard
Topics
SQL Schema
Pandas Schema
Table: Listens

+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| user_id     | int     |
| song_id     | int     |
| day         | date    |
+-------------+---------+
This table may contain duplicate rows.
Each row of this table indicates that the user user_id listened to the song song_id on the day day.


Table: Friendship

+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| user1_id      | int     |
| user2_id      | int     |
+---------------+---------+
(user1_id, user2_id) is the primary key (combination of columns with unique values) for this table.
Each row of this table indicates that the users user1_id and user2_id are friends.
Note that user1_id < user2_id.


Write a solution to report the similar friends of Leetcodify users. A user x and user y are similar friends if:

Users x and y are friends, and
Users x and y listened to the same three or more different songs on the same day.
Return the result table in any order. Note that you must return the similar pairs of friends the same way they were represented in the input (i.e., always user1_id < user2_id).

The result format is in the following example.



Example 1:

Input:
Listens table:
+---------+---------+------------+
| user_id | song_id | day        |
+---------+---------+------------+
| 1       | 10      | 2021-03-15 |
| 1       | 11      | 2021-03-15 |
| 1       | 12      | 2021-03-15 |
| 2       | 10      | 2021-03-15 |
| 2       | 11      | 2021-03-15 |
| 2       | 12      | 2021-03-15 |
| 3       | 10      | 2021-03-15 |
| 3       | 11      | 2021-03-15 |
| 3       | 12      | 2021-03-15 |
| 4       | 10      | 2021-03-15 |
| 4       | 11      | 2021-03-15 |
| 4       | 13      | 2021-03-15 |
| 5       | 10      | 2021-03-16 |
| 5       | 11      | 2021-03-16 |
| 5       | 12      | 2021-03-16 |
+---------+---------+------------+
Friendship table:
+----------+----------+
| user1_id | user2_id |
+----------+----------+
| 1        | 2        |
| 2        | 4        |
| 2        | 5        |
+----------+----------+
Output:
+----------+----------+
| user1_id | user2_id |
+----------+----------+
| 1        | 2        |
+----------+----------+
Explanation:
Users 1 and 2 are friends, and they listened to songs 10, 11, and 12 on the same day. They are similar friends.
Users 1 and 3 listened to songs 10, 11, and 12 on the same day, but they are not friends.
Users 2 and 4 are friends, but they did not listen to the same three different songs.
Users 2 and 5 are friends and listened to songs 10, 11, and 12, but they did not listen to them on the same day.
  1. # Write your MySQL query statement below
  2. WITH L AS (
  3.     SELECT DISTINCT user_id, song_id, day
  4.     FROM Listens
  5. )
  6. SELECT DISTINCT
  7.     f.user1_id,
  8.     f.user2_id
  9. FROM Friendship f
  10. JOIN L a
  11.   ON a.user_id = f.user1_id
  12. JOIN L b
  13.   ON b.user_id = f.user2_id
  14. AND b.day = a.day
  15. AND b.song_id = a.song_id
  16. GROUP BY f.user1_id, f.user2_id, a.day
  17. HAVING COUNT(*) >= 3;
复制代码
题目大意

找出"相似好友":用户 x 和 y 需要同时满足两个条件:

两人是好友。
两人在同一天听过至少 3 首相同的不同歌曲。

输出的好友对必须和 Friendship 表里的存储方式一致,也就是 user1_id < user2_id。

和上一题(1917)的关系

这道题就是 1917 把"好友"条件反过来:

        1917 推荐好友        1919 相似好友
好友关系        必须不是好友        必须是好友
驱动表        Listens 自连接,再排除好友        直接从 Friendship 出发
输出方向        双向 (x,y) 和 (y,x)        单向,user1_id < user2_id

因为直接从 Friendship 出发,所以不需要 NOT EXISTS,也不需要 LEAST / GREATEST 规范化。好友对本来就只存一行,且 user1_id < user2_id,正好是题目要求的输出格式。

解法思路
第一步:去重(CTE L)
sql
SELECT DISTINCT user_id, song_id, day FROM Listens

Listens 可能有重复行。题目要的是 3 首不同的歌,所以先去重,之后 COUNT(*) 就等于共同歌曲的数量。

第二步:好友对分别连接两人的收听记录
sql
FROM Friendship f
JOIN L a ON a.user_id = f.user1_id
JOIN L b ON b.user_id = f.user2_id
        AND b.day = a.day
        AND b.song_id = a.song_id
a 是 user1_id 的收听记录,b 是 user2_id 的收听记录。
连接条件要求 day 和 song_id 都相同,所以每个结果行代表"两人同一天都听过的一首歌"。
第三步:按天分组,统计共同歌曲数
sql
GROUP BY f.user1_id, f.user2_id, a.day
HAVING COUNT(*) >= 3

分组里必须带上 a.day,这和 1917 是同一个陷阱:

带 day:3 首歌必须在同一天听,符合题意。
不带 day:不同日期的歌会被加在一起,示例里的 (2, 5) 就会被错误地算成相似好友。

示例中用户 2 和 5 是好友,听的歌也相同(10、11、12),但日期不同(3 月 15 日和 16 日),所以不能算。

第四步:最外层 DISTINCT

同一对好友可能在多个不同的日子都满足 3 首歌的条件,会产生多行,所以要用 DISTINCT 去重。

用示例走一遍
好友对        情况        结果
1 和 2        3 月 15 日都听了 10、11、12        保留
2 和 4        只共同听了 10、11,共 2 首        不够 3 首
2 和 5        歌曲相同,但日期不同        不在同一天

用户 1 和 3 虽然也听了相同的 3 首歌,但他们不是好友,Friendship 里没有这一对,所以根本不会出现在连接结果里。最终只输出 (1, 2)。
回复

使用道具 举报

🔗
 楼主| Myron2017 6 小时前 | 只看该作者
全局:
1934. Confirmation Rate
Medium
Topics
conpanies icon
Companies
SQL Schema
Pandas Schema
Table: Signups

+----------------+----------+
| Column Name    | Type     |
+----------------+----------+
| user_id        | int      |
| time_stamp     | datetime |
+----------------+----------+
user_id is the column of unique values for this table.
Each row contains information about the signup time for the user with ID user_id.


Table: Confirmations

+----------------+----------+
| Column Name    | Type     |
+----------------+----------+
| user_id        | int      |
| time_stamp     | datetime |
| action         | ENUM     |
+----------------+----------+
(user_id, time_stamp) is the primary key (combination of columns with unique values) for this table.
user_id is a foreign key (reference column) to the Signups table.
action is an ENUM (category) of the type ('confirmed', 'timeout')
Each row of this table indicates that the user with ID user_id requested a confirmation message at time_stamp and that confirmation message was either confirmed ('confirmed') or expired without confirming ('timeout').


The confirmation rate of a user is the number of 'confirmed' messages divided by the total number of requested confirmation messages. The confirmation rate of a user that did not request any confirmation messages is 0. Round the confirmation rate to two decimal places.

Write a solution to find the confirmation rate of each user.

Return the result table in any order.

The result format is in the following example.



Example 1:

Input:
Signups table:
+---------+---------------------+
| user_id | time_stamp          |
+---------+---------------------+
| 3       | 2020-03-21 10:16:13 |
| 7       | 2020-01-04 13:57:59 |
| 2       | 2020-07-29 23:09:44 |
| 6       | 2020-12-09 10:39:37 |
+---------+---------------------+
Confirmations table:
+---------+---------------------+-----------+
| user_id | time_stamp          | action    |
+---------+---------------------+-----------+
| 3       | 2021-01-06 03:30:46 | timeout   |
| 3       | 2021-07-14 14:00:00 | timeout   |
| 7       | 2021-06-12 11:57:29 | confirmed |
| 7       | 2021-06-13 12:58:28 | confirmed |
| 7       | 2021-06-14 13:59:27 | confirmed |
| 2       | 2021-01-22 00:00:00 | confirmed |
| 2       | 2021-02-28 23:59:59 | timeout   |
+---------+---------------------+-----------+
Output:
+---------+-------------------+
| user_id | confirmation_rate |
+---------+-------------------+
| 6       | 0.00              |
| 3       | 0.00              |
| 7       | 1.00              |
| 2       | 0.50              |
+---------+-------------------+
Explanation:
User 6 did not request any confirmation messages. The confirmation rate is 0.
User 3 made 2 requests and both timed out. The confirmation rate is 0.
User 7 made 3 requests and all were confirmed. The confirmation rate is 1.
User 2 made 2 requests where one was confirmed and the other timed out. The confirmation rate is 1 / 2 = 0.5.
  1. # Write your MySQL query statement below
  2. SELECT
  3.     s.user_id,
  4.     ROUND(IFNULL(AVG(c.action = 'confirmed'), 0), 2) AS confirmation_rate
  5. FROM Signups s
  6. LEFT JOIN Confirmations c
  7.   ON c.user_id = s.user_id
  8. GROUP BY s.user_id;
复制代码
题目大意

对每个用户计算确认率:confirmed 的消息数 ÷ 请求的确认消息总数,保留两位小数。没有请求过任何确认消息的用户,确认率为 0。

解法思路
第一步:从 Signups 出发做 LEFT JOIN

这道题最容易错的地方就在这里。示例里用户 6 注册了,但 Confirmations 里没有他的任何记录:

如果从 Confirmations 出发,或者用 INNER JOIN,用户 6 会直接消失,结果少一行。
从 Signups 出发做 LEFT JOIN,所有注册用户都会保留。没有请求记录的用户,c.* 全是 NULL。
第二步:用 AVG 巧妙地算比例

c.action = 'confirmed' 在 MySQL 里的结果是 1(真)、0(假)或 NULL(action 本身是 NULL)。对这些值取平均:

有请求记录的用户:AVG 就是 confirmed 的个数 ÷ 总请求数,正好是确认率。
没有请求记录的用户(用户 6):只有一行 NULL,AVG 会忽略 NULL,结果是 NULL。
第三步:处理没有请求的用户并四舍五入
sql
ROUND(IFNULL(AVG(...), 0), 2)

IFNULL(..., 0) 把用户 6 的 NULL 换成 0,ROUND(..., 2) 保留两位小数,所以输出是 0.00、0.50、1.00 这样的格式。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号
隐私提醒:
  • ☑ 禁止发布广告,拉群,贴个人联系方式:找人请去🔗同学同事飞友,拉群请去🔗拉群结伴,广告请去🔗跳蚤市场,和 🔗租房广告|找室友
  • ☑ 论坛内容在发帖 30 分钟内可以编辑,过后则不能删帖。为防止被骚扰甚至人肉,不要公开留微信等联系方式,如有需求请以论坛私信方式发送。
  • ☑ 干货版块可免费使用 🔗超级匿名:面经(美国面经、中国面经、数科面经、PM面经),抖包袱(美国、中国)和录取汇报、定位选校版
  • ☑ 查阅全站 🔗各种匿名方法

本版积分规则

>
快速回复 返回顶部 返回列表