1. 题目解析与需求理解
这道LeetCode数据库题目编号1148,标题为"文章浏览 I"。题目要求我们找出那些既是文章作者又是文章浏览者的用户ID,并按升序排列返回结果。
在实际业务场景中,这种需求很常见。比如在一个内容平台(博客、论坛等)中,我们可能需要分析:
- 哪些用户不仅创作内容,还会浏览其他用户的内容
- 作者对自己文章的查看行为(可能是检查发布效果或统计数据)
- 平台核心用户的互动情况(既生产内容又消费内容的用户)
2. 数据表结构与字段分析
根据题目描述,我们有一个名为Views的表,至少包含以下字段:
author_id:文章作者的用户IDviewer_id:文章浏览者的用户ID
虽然没有给出完整的表结构,但根据SQL查询可以推断出这些基本信息。在实际工作中,这类表通常还会有:
article_id:文章IDview_date:浏览时间- 可能的其他元数据字段
3. SQL查询详解
3.1 基础查询逻辑
题目给出的解决方案是:
sql复制SELECT DISTINCT author_id AS id
FROM Views
WHERE author_id = viewer_id
ORDER BY id ASC;
让我们拆解这个查询的每个部分:
WHERE author_id = viewer_id:这是核心筛选条件,找出作者和浏览者是同一个人的记录SELECT DISTINCT author_id AS id:选择作者ID字段,去重后命名为idORDER BY id ASC:按ID升序排列结果
3.2 为什么需要DISTINCT
在实际数据中,一个作者可能浏览自己的多篇文章,会产生多条记录。例如:
- 作者ID=1浏览了自己写的3篇文章
- 这样会有3条author_id=1且viewer_id=1的记录
使用DISTINCT可以确保最终结果中每个作者ID只出现一次,避免重复。
3.3 字段别名的使用
将author_id重命名为id是为了满足题目要求的输出格式。在实际项目中,保持字段命名一致性很重要:
- 如果API或前端期望字段名为"id",这种重命名就很必要
- 也可以提高结果集的可读性
4. 查询优化思考
4.1 性能考量
对于大型平台,Views表可能非常庞大(数百万甚至上亿条记录)。这时我们需要考虑查询性能:
-
索引设计:
- 在
author_id和viewer_id上创建复合索引 - 或者单独为这两个字段创建索引
- 对于这个特定查询,(author_id, viewer_id)的复合索引最有效
- 在
-
执行计划分析:
- 使用
EXPLAIN查看查询执行计划 - 确保查询使用了正确的索引
- 检查是否出现了全表扫描
- 使用
4.2 替代写法
除了题目给出的解法,还有其他写法可以达到相同效果:
sql复制-- 使用GROUP BY替代DISTINCT
SELECT author_id AS id
FROM Views
WHERE author_id = viewer_id
GROUP BY author_id
ORDER BY id ASC;
-- 使用子查询
SELECT DISTINCT id
FROM (
SELECT author_id AS id
FROM Views
WHERE author_id = viewer_id
) AS self_viewers
ORDER BY id ASC;
在大多数现代数据库系统中,这三种写法的性能差异不大,查询优化器会生成相似的执行计划。
5. 实际应用扩展
5.1 业务分析场景
这个简单查询可以扩展出很多有价值的业务分析:
-
作者自浏览比例:
sql复制SELECT COUNT(DISTINCT CASE WHEN author_id = viewer_id THEN author_id END) * 100.0 / COUNT(DISTINCT author_id) AS self_view_percentage FROM Views; -
高频自浏览作者:
sql复制SELECT author_id AS id, COUNT(*) AS self_view_count FROM Views WHERE author_id = viewer_id GROUP BY author_id ORDER BY self_view_count DESC LIMIT 10;
5.2 数据质量检查
这类查询也可以用于数据质量检查:
- 检测是否有异常的自浏览记录(如短时间内大量自浏览)
- 识别可能的刷量行为
- 验证用户行为的真实性
6. 常见问题与解决方案
6.1 空结果处理
如果查询返回空结果,可能的原因:
- 确实没有作者浏览自己文章的情况
- 表数据有问题(如author_id和viewer_id字段不匹配)
- 查询条件写错(如大小写问题)
解决方案:
- 先检查数据是否存在:
SELECT * FROM Views LIMIT 10; - 验证条件逻辑:
SELECT COUNT(*) FROM Views WHERE author_id = viewer_id;
6.2 性能问题
如果查询执行缓慢:
- 检查表大小:
SELECT COUNT(*) FROM Views; - 查看索引情况:
SHOW INDEX FROM Views; - 分析执行计划:
EXPLAIN SELECT ...
优化建议:
- 添加适当的索引
- 考虑分批处理大数据集
- 在非高峰期执行查询
6.3 结果排序异常
如果结果排序不符合预期:
- 检查
ORDER BY子句是否正确 - 确认是
ASC(升序)还是DESC(降序) - 注意字符型ID的排序可能与数值型不同
7. 不同数据库的语法差异
虽然题目使用标准SQL,但不同数据库系统有些细微差别:
7.1 MySQL/MariaDB
- 完全支持题目中的语法
- 推荐使用InnoDB引擎,对这类查询优化较好
7.2 PostgreSQL
- 语法相同
- 对DISTINCT操作有特别优化
7.3 SQL Server
- 语法相同
- 可以使用
WITH(NOLOCK)提示处理大表
7.4 Oracle
- 需要使用
/*+ INDEX(...) */提示来强制索引使用 - 表名可能需要包含模式名
8. 学习建议与进阶方向
对于想深入学习SQL的开发者:
-
理解执行计划:
- 学习使用
EXPLAIN或EXPLAIN ANALYZE - 理解不同的访问方法(全表扫描、索引扫描等)
- 学习使用
-
性能优化:
- 学习索引设计原则
- 了解查询重写技巧
- 掌握分区表的使用
-
业务分析:
- 将SQL查询与业务问题结合
- 学习使用窗口函数进行复杂分析
- 掌握常见的数据分析模式
-
实践建议:
- 在LeetCode上多练习类似题目
- 尝试在实际项目中应用这些技巧
- 参与开源项目贡献SQL相关代码