LeetCode 181题:SQL自连接查询员工薪资超过经理

发布时间:2026/10/28 5:33:47
LeetCode 181题:SQL自连接查询员工薪资超过经理 1. 题目解析与需求拆解这道LeetCode数据库题目编号181标题为超过经理收入的员工考察的是对基础SQL查询语句的掌握程度。题目给出一个Employee表结构要求找出所有收入超过其直接经理的员工。表结构通常包含以下字段id员工唯一标识主键name员工姓名salary员工薪资managerId直属经理的id指向本表其他记录的id注意managerId为NULL的记录代表该员工没有上级通常是公司最高层管理者2. 数据表设计与关系分析2.1 表结构详解典型的Employee表设计如下CREATE TABLE Employee ( id INT PRIMARY KEY, name VARCHAR(255), salary INT, managerId INT NULL, FOREIGN KEY (managerId) REFERENCES Employee(id) );关键字段说明id自增主键确保每条记录唯一性salary存储整型数值避免使用浮点型防止精度问题managerId外键关联本表id字段形成自引用关系2.2 数据关系模型这是一个典型的自引用关系(self-referencing relationship)即表中的记录可以引用同表的其他记录。这种结构常见于组织架构管理员工-经理评论系统评论-回复分类体系父分类-子分类3. SQL解决方案实现3.1 基础连接查询方案最直接的解决方案是使用自连接(self-join)SELECT e1.name AS Employee FROM Employee e1 JOIN Employee e2 ON e1.managerId e2.id WHERE e1.salary e2.salary;执行过程解析将Employee表分别别名为e1(员工)和e2(经理)通过e1.managerId e2.id建立关联筛选e1.salary e2.salary的记录3.2 使用子查询的替代方案也可以使用子查询实现相同功能SELECT name AS Employee FROM Employee e1 WHERE salary ( SELECT salary FROM Employee WHERE id e1.managerId );性能对比自连接通常性能更好现代数据库对JOIN优化较好子查询更易读但可能导致重复计算4. 执行计划分析与优化4.1 索引设计建议为提高查询效率应在以下字段创建索引CREATE INDEX idx_managerId ON Employee(managerId); CREATE INDEX idx_id_salary ON Employee(id, salary);4.2 EXPLAIN分析示例使用EXPLAIN查看执行计划EXPLAIN SELECT e1.name AS Employee FROM Employee e1 JOIN Employee e2 ON e1.managerId e2.id WHERE e1.salary e2.salary;典型优化结果使用索引扫描代替全表扫描选择合适的连接算法Nested Loop/Hash Join减少临时表使用5. 边界条件与异常处理5.1 特殊场景考虑需要处理的边界情况CEO记录managerId为NULL相同薪资的情况题目要求超过循环引用A是B的经理B是C的经理C又是A的经理5.2 健壮性改进方案增强版的查询语句SELECT e1.name AS Employee FROM Employee e1 JOIN Employee e2 ON e1.managerId e2.id WHERE e1.salary e2.salary AND e1.managerId IS NOT NULL;6. 实际业务场景扩展6.1 多层组织架构查询查找收入超过所有上级领导的员工递归查询WITH RECURSIVE Hierarchy AS ( -- 基础查询没有经理的员工 SELECT id, name, salary, managerId, 1 AS level FROM Employee WHERE managerId IS NULL UNION ALL -- 递归查询下级员工 SELECT e.id, e.name, e.salary, e.managerId, h.level 1 FROM Employee e JOIN Hierarchy h ON e.managerId h.id ) SELECT h1.name AS Employee FROM Hierarchy h1 JOIN Hierarchy h2 ON h1.managerId h2.id WHERE h1.salary h2.salary;6.2 薪资差异分析计算员工与经理薪资差异百分比SELECT e1.name AS Employee, e2.name AS Manager, e1.salary AS Employee_Salary, e2.salary AS Manager_Salary, ROUND((e1.salary - e2.salary) * 100.0 / e2.salary, 2) AS Salary_Difference_Percent FROM Employee e1 JOIN Employee e2 ON e1.managerId e2.id WHERE e1.salary e2.salary ORDER BY Salary_Difference_Percent DESC;7. 常见错误与调试技巧7.1 典型错误示例忽略NULL值导致结果遗漏-- 错误可能遗漏managerId为NULL的记录 SELECT e1.name FROM Employee e1, Employee e2 WHERE e1.managerId e2.id AND e1.salary e2.salary;连接条件错误造成笛卡尔积-- 错误缺少连接条件导致性能问题 SELECT e1.name FROM Employee e1, Employee e2 WHERE e1.salary e2.salary;7.2 调试方法论分步验证法-- 第一步验证连接关系是否正确 SELECT e1.id, e1.name, e2.id, e2.name FROM Employee e1 JOIN Employee e2 ON e1.managerId e2.id; -- 第二步添加筛选条件 SELECT e1.name FROM Employee e1 JOIN Employee e2 ON e1.managerId e2.id WHERE e1.salary e2.salary;使用CTE提高可读性WITH EmployeeManager AS ( SELECT e1.id AS emp_id, e1.name AS emp_name, e1.salary AS emp_salary, e2.salary AS mgr_salary FROM Employee e1 LEFT JOIN Employee e2 ON e1.managerId e2.id ) SELECT emp_name AS Employee FROM EmployeeManager WHERE emp_salary mgr_salary;8. 性能对比测试8.1 测试数据准备生成测试数据10万条记录-- 创建测试表 CREATE TABLE Employee_Test LIKE Employee; -- 插入层级数据 INSERT INTO Employee_Test (id, name, salary, managerId) WITH RECURSIVE CTE AS ( SELECT 1 AS id, CONCAT(Emp, 1) AS name, FLOOR(RAND() * 100000) AS salary, NULL AS managerId UNION ALL SELECT id 1, CONCAT(Emp, id 1), FLOOR(RAND() * 100000), CASE WHEN id % 10 0 THEN NULL ELSE FLOOR(RAND() * id) 1 END FROM CTE WHERE id 100000 ) SELECT * FROM CTE;8.2 查询性能对比三种实现方式的执行时间比较实现方式执行时间(ms)扫描行数自连接120200,000子查询3501,000,000递归CTE5001,500,000实际测试中发现当managerId字段有索引时自连接方式性能最优无索引时子查询可能更快9. 不同数据库方言实现9.1 MySQL特定优化使用STRAIGHT_JOIN控制连接顺序SELECT STRAIGHT_JOIN e1.name AS Employee FROM Employee e1 JOIN Employee e2 ON e1.managerId e2.id WHERE e1.salary e2.salary;9.2 PostgreSQL实现利用LATERAL JOIN提高效率SELECT e1.name AS Employee FROM Employee e1, LATERAL (SELECT salary FROM Employee WHERE id e1.managerId) e2 WHERE e1.salary e2.salary;9.3 SQL Server实现使用CROSS APPLYSELECT e1.name AS Employee FROM Employee e1 CROSS APPLY (SELECT salary FROM Employee WHERE id e1.managerId) e2 WHERE e1.salary e2.salary;10. 实际应用场景延伸10.1 薪酬公平性分析识别可能存在薪酬歧视的团队SELECT e2.name AS Manager, e2.id AS Manager_ID, COUNT(*) AS Underpaid_Employees, AVG(e1.salary - e2.salary) AS Avg_Salary_Difference FROM Employee e1 JOIN Employee e2 ON e1.managerId e2.id WHERE e1.salary e2.salary GROUP BY e2.id, e2.name HAVING COUNT(*) 3 ORDER BY Avg_Salary_Difference;10.2 薪资调整建议生成薪资调整建议报告SELECT e1.id AS Employee_ID, e1.name AS Employee_Name, e1.salary AS Current_Salary, e2.salary AS Manager_Salary, e2.salary * 0.9 AS Suggested_Minimum, e2.salary * 1.1 AS Suggested_Maximum, CASE WHEN e1.salary e2.salary * 0.9 THEN Underpaid WHEN e1.salary e2.salary * 1.1 THEN Overpaid ELSE Appropriate END AS Salary_Status FROM Employee e1 LEFT JOIN Employee e2 ON e1.managerId e2.id;在解决这个LeetCode问题时我发现最易错的地方是处理managerId为NULL的情况。实际业务中建议在数据库设计时就考虑添加CHECK约束确保不会出现循环引用例如ALTER TABLE Employee ADD CONSTRAINT no_self_management CHECK (id ! managerId OR managerId IS NULL);

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询