LeetCode的SQL题刷到602《好友申请 II》时,我第一反应是“又是求最大值的聚合题,应该不难”。结果真正动手写的时候才发现,这道题和常见的“按某列分组求总数”不太一样,难点不在语法,而在于你怎么把“好友关系”这个业务概念翻译成SQL里的行。刷题群里也经常有人问:为什么写出来的结果只有发申请的人?为什么用UNION以后数字全部变成1?其实都是同一个问题——好友关系是双向的,但表里只存了一条记录。
这道题对准备面试的同学、日常写SQL的分析师和开发都很友好。它考察的不仅是GROUP BY和聚合函数的用法,更考验你能不能把一个隐含的映射关系,也就是“一条好友申请记录 = 两个用户的好友数各加1”,拆成两个方向来处理。搞清楚这个套路之后,你会发现类似“谁互动最多”“哪个账户进出最活跃”这类业务问题都能顺手解决。下面我把这道题的完整拆解、标准写法、平局处理方案和易错点一次讲清楚。
1. 先读懂这张好友申请表:题目到底在问什么
1.1 表结构和示例数据
题目给的表叫 RequestAccepted,意思是被接受的好友申请。结构很简单,三列:
sql复制+----------------+---------+
| Column Name | Type |
+----------------+---------+
| requester_id | int |
| accepter_id | int |
| accept_date | date |
+----------------+---------+
主键是 (requester_id, accepter_id)。也就是说,同一对用户之间只会存在一条申请记录,不会出现完全相同的两行。我用官方示例跑一下:
sql复制+--------------+-------------+-------------+
| requester_id | accepter_id | accept_date |
+--------------+-------------+-------------+
| 1 | 2 | 2016/06/03 |
| 1 | 3 | 2016/06/08 |
| 2 | 3 | 2016/06/08 |
| 3 | 4 | 2016/06/09 |
+--------------+-------------+-------------+
这是一个非常典型的“关系表”:每一行代表用户 requester_id 向 accepter_id 发了好友申请,并且对方接受了。注意,表里只有一条记录,但从好友关系的语义来说,用户1和用户2一旦成为好友,这层关系对双方是同时成立的。也就是说,这一行不仅给用户1的好友数加1,也要给用户2的好友数加1。
1.2 需求拆解:好友关系是“双向”的
题目要求很简单:找出好友数最多的用户,返回他的 id 和好友数 num。
那什么是“好友数”?一个用户可能作为 requester_id 发出申请,也可能作为 accepter_id 接受别人的申请。无论哪种角色,只要这条记录出现在 RequestAccepted 表里,就说明这对用户之间已经建立了真实的好友关系。所以一个用户的总好友数,一定等于他“发出去并被接受的好友申请数”加上“收到并接受的好友申请数”。
这就是这道题最核心的坎:表里一行记录只能通过某个固定字段归属到一个用户头上,但业务上它影响的是两个用户。如果不做处理,只盯着 requester_id 或者只盯着 accepter_id,都会漏掉一半的好友关系。后面很多错误答案都是从这里开始的。
1.3 示例数据手工推演
我习惯在做SQL题之前先手工把结果算一遍,这样写出来的SQL可以对拍。以示例数据为例:
- 用户1:作为请求者出现2次(对应2、3),作为接受者出现0次,所以好友数是2。
- 用户2:作为请求者出现1次(对应3),作为接受者出现1次(来自1),好友数是2。
- 用户3:作为请求者出现1次(对应4),作为接受者出现2次(来自1、2),好友数是3。
- 用户4:作为请求者出现0次,作为接受者出现1次(来自3),好友数是1。
结果很清楚:用户3的好友数最多,为3。题目要求输出:
sql复制+-----
