1. 问题背景与需求解析
这道LeetCode数据库题目编号584,标题为"寻找用户推荐人",属于典型的SQL查询类问题。这类题目在实际面试中经常出现,主要考察开发者对基础SQL语法的掌握程度,特别是对NULL值处理和条件筛选的理解。
问题的核心需求是:给定一个包含用户信息的表,其中包含用户ID、姓名以及推荐人ID(即该用户是由哪个已有用户推荐注册的),要求编写SQL查询找出所有没有被特定用户(ID=2)推荐的用户列表。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据表结构与业务场景分析
2.1 典型用户表结构
根据题目描述,我们可以推测出表结构大致如下:
sql复制CREATE TABLE customer (
id INT PRIMARY KEY,
name VARCHAR(255),
referee_id INT
);
其中:
id:用户唯一标识name:用户姓名referee_id:推荐该用户的推荐人ID,这是一个外键,指向同一表中的id字段
2.2 实际业务场景
这种数据结构在以下场景中很常见:
- 用户推荐系统:电商平台、社交网络等需要追踪用户来源的场景
- 多级分销系统:记录上下级关系
- 邀请注册机制:追踪每个用户的邀请人
在实际业务中,referee_id字段通常允许为NULL,表示该用户可能是自然流量或通过其他渠道注册,而非通过用户推荐。
3. SQL解决方案详解
3.1 基础解法与NULL值陷阱
最直观的解法可能是:
sql复制SELECT name
FROM customer
WHERE referee_id != 2;
但这种写法存在严重问题:它无法正确处理referee_id为NULL的情况。在SQL中,任何与NULL的比较操作(包括=、!=、>、<等)都会返回UNKNOWN而非TRUE或FALSE,这些行会被WHERE条件过滤掉。
3.2 正确的NULL值处理方案
正确的解决方案应该显式处理NULL值:
sql复制SELECT name
FROM customer
WHERE referee_id != 2 OR referee_id IS NULL;
这个查询的逻辑是:
- 筛选出所有referee_id不等于2的记录
- 同时包含referee_id为NULL的记录
3.3 替代写法比较
还有其他几种等效写法:
sql复制-- 使用NOT IN
SELECT name
FROM customer
WHERE referee_id NOT IN (2) OR referee_id IS NULL;
-- 使用COALESCE函数
SELECT name
FROM customer
WHERE COALESCE(referee_id, 0) != 2;
注意:COALESCE写法虽然简洁,但改变了原始数据的语义,且当referee_id实际为0时会产生误判,不推荐在生产环境使用。
4. 执行计划与性能考量
4.1 索引设计建议
对于这类查询,合理的索引设计能显著提升性能:
sql复制CREATE INDEX idx_referee ON customer(referee_id);
这个索引可以加速WHERE条件中对referee_id的筛选操作。
4.2 不同写法的执行效率
在MySQL中测试发现:
- 原始写法(referee_id != 2 OR referee_id IS NULL)通常能利用索引
- NOT IN写法在某些数据库版本中可能无法使用索引
- COALESCE写法会导致索引失效
5. 常见错误与调试技巧
5.1 典型错误案例
-
忽略NULL值:
sql复制-- 错误:会漏掉referee_id为NULL的记录 SELECT name FROM customer WHERE referee_id != 2; -
错误使用NOT IN:
sql复制-- 错误:NOT IN (NULL)会导致整个表达式返回NULL SELECT name FROM customer WHERE referee_id NOT IN (2, NULL);
5.2 调试建议
-
先确认数据分布:
sql复制SELECT referee_id, COUNT(*) FROM customer GROUP BY referee_id; -
使用CASE语句验证逻辑:
sql复制SELECT name, referee_id, CASE WHEN referee_id != 2 OR referee_id IS NULL THEN 'Y' ELSE 'N' END AS should_include FROM customer;
6. 业务场景扩展
6.1 多条件推荐人筛选
实际业务中可能需要更复杂的筛选条件,例如:
sql复制-- 查找既不是2也不是3推荐的用户
SELECT name
FROM customer
WHERE referee_id NOT IN (2, 3) OR referee_id IS NULL;
6.2 推荐关系层级查询
可以扩展查询推荐关系的层级:
sql复制-- 查找用户及其推荐人信息
SELECT c1.name AS customer_name, c2.name AS referee_name
FROM customer c1
LEFT JOIN customer c2 ON c1.referee_id = c2.id;
7. 不同数据库的实现差异
7.1 MySQL/MariaDB
MySQL处理NULL值的逻辑比较严格,必须显式使用IS NULL判断。
7.2 PostgreSQL
PostgreSQL提供了IS DISTINCT FROM语法,可以更简洁地处理NULL比较:
sql复制SELECT name
FROM customer
WHERE referee_id IS DISTINCT FROM 2;
7.3 SQL Server
SQL Server与MySQL类似,但执行计划可能有所不同,特别是在处理OR条件时。
8. 最佳实践总结
- 始终考虑NULL值的特殊情况
- 对于可能为NULL的列,避免直接使用!=或=比较
- 使用IS NULL或IS NOT NULL显式判断
- 在复杂查询中,考虑使用COALESCE或IFNULL函数提供默认值
- 为经常查询的外键字段建立索引
- 在生产环境中,先在小数据集上验证查询逻辑
9. 类似题目练习建议
为了巩固这类问题的解决能力,可以尝试以下LeetCode题目:
-
- 从不订购的客户
-
- 销售员
-
- 文章浏览I
-
- 上升的温度
这些题目都涉及类似的NULL值处理和条件筛选技巧。
