1. 题目背景与需求分析
今天我们来拆解LeetCode数据库题目614号"二级关注者"(Second Degree Follower)。这是一道考察SQL多表关联和分组统计的中等难度题目,在实际业务场景中也非常常见——社交网络中的好友推荐系统、内容分发系统的潜在受众分析等场景都会用到类似的逻辑。
题目核心需求是:找出所有被至少两个人关注的二级关注者。这里的"二级关注者"定义需要特别注意——不是直接关注你的人,而是你关注的人所关注的那些用户。举个例子:如果用户A关注了用户B,用户B又关注了用户C,那么对A来说,C就是二级关注者。
2. 数据模型理解与表结构设计
首先我们需要理解题目给出的数据模型。根据LeetCode题目描述,主要涉及一张表:
CREATE TABLE follow ( followee VARCHAR(255), follower VARCHAR(255), PRIMARY KEY(followee, follower) )这个表结构非常简单但信息量足够:
followee:被关注者follower:关注者- 联合主键确保不会出现重复的关注关系
在实际业务中,这样的设计能有效防止用户重复关注同一个对象。值得注意的是,这个模型是单向关注关系(类似Twitter),而不是双向好友关系(类似微信)。
3. 解题思路拆解
要解决这个问题,我们需要分步骤思考:
3.1 第一步:找出所有二级关注关系
我们需要先找出每个人的二级关注者。这需要做一次自连接:
SELECT f1.follower AS user, f2.followee AS second_degree_follower FROM follow f1 JOIN follow f2 ON f1.followee = f2.follower这个查询的逻辑是:
- f1表记录用户直接关注的人(一级关系)
- f2表记录一级关注者所关注的人(二级关系)
- 通过f1.followee = f2.follower的连接条件,建立二级关系链
3.2 第二步:统计每个二级关注者的关注人数
接下来我们需要对上述结果进行分组统计:
SELECT second_degree_follower AS follower, COUNT(DISTINCT user) AS num FROM ( SELECT f1.follower AS user, f2.followee AS second_degree_follower FROM follow f1 JOIN follow f2 ON f1.followee = f2.follower ) AS t GROUP BY second_degree_follower这里有几个关键点:
- 使用DISTINCT确保同一个用户不会被重复计算
- 统计每个二级关注者被多少不同的用户间接关注
- 使用子查询使逻辑更清晰
3.3 第三步:筛选符合条件的记录
最后我们只需要筛选出被至少2人关注的二级关注者:
SELECT second_degree_follower AS follower, num FROM ( SELECT second_degree_follower, COUNT(DISTINCT user) AS num FROM ( SELECT f1.follower AS user, f2.followee AS second_degree_follower FROM follow f1 JOIN follow f2 ON f1.followee = f2.follower ) AS t1 GROUP BY second_degree_follower ) AS t2 WHERE num >= 24. 优化与变体思考
4.1 性能优化考虑
在实际大数据量场景下,这个查询可能会有性能问题。我们可以考虑以下优化:
- 索引优化:确保(followee,follower)和(follower,followee)都有复合索引
- 避免DISTINCT:如果业务允许,可以考虑用其他方式去重
- 使用CTE提高可读性:
WITH second_degree AS ( SELECT f1.follower AS user, f2.followee AS second_degree_follower FROM follow f1 JOIN follow f2 ON f1.followee = f2.follower ) SELECT second_degree_follower AS follower, COUNT(DISTINCT user) AS num FROM second_degree GROUP BY second_degree_follower HAVING COUNT(DISTINCT user) >= 24.2 业务场景扩展
这个模式可以扩展到很多实际业务场景:
- 社交推荐系统:基于共同关注推荐可能认识的人
- 内容分发:找出用户可能感兴趣的二级关系内容
- 风险控制:识别潜在的虚假账号网络
5. 常见错误与调试技巧
在解决这类问题时,新手常犯的错误包括:
- 忽略自连接的本质:忘记连接条件会导致笛卡尔积
- 错误理解关系方向:混淆follower和followee的角色
- 重复计数:忘记使用DISTINCT导致重复统计
- 筛选条件位置错误:把HAVING条件误放在WHERE中
调试技巧:
- 先单独运行最内层查询,验证基础关系
- 逐步添加外层逻辑,每步检查中间结果
- 使用LIMIT限制结果集大小进行快速验证
6. 完整解决方案
综合以上分析,最终的解决方案如下:
SELECT follower, num FROM ( SELECT second_degree_follower AS follower, COUNT(DISTINCT user) AS num FROM ( SELECT f1.follower AS user, f2.followee AS second_degree_follower FROM follow f1 JOIN follow f2 ON f1.followee = f2.follower WHERE f1.follower != f2.followee -- 避免自关注的情况 ) AS t1 GROUP BY second_degree_follower ) AS t2 WHERE num >= 2 ORDER BY follower这个方案增加了两个优化:
- 添加了WHERE条件排除用户关注自己的情况
- 增加了ORDER BY使结果更规整
7. 实际业务中的应用思考
在实际工程实践中,这类查询往往需要进一步优化:
- 预计算:对于不常变动的社交关系,可以定期预计算并存储结果
- 图数据库:超大规模关系网络更适合使用Neo4j等图数据库
- 批处理:可以设计增量更新的批处理作业,避免全量计算
对于LeetCode这样的平台,理解基础SQL模式是关键,但在实际工作中,我们需要根据数据规模和业务需求选择合适的实现方式。