应使用DENSE_RANK()按hire_date升序排名司龄,因同日入职者司龄相同须并列且无空位;须先WHERE过滤在职员工再排名,避免序号断裂。

用 ROW_NUMBER() 按入职日期倒序排司龄名次
司龄越长,排名越靠前,所以必须按 hire_date 升序排——因为早入职的人司龄大,但数值小;直接用 ORDER BY hire_date DESC 会把最新入职的排第一,结果完全相反。正确做法是升序排列后用 ROW_NUMBER() 赋值:
SELECT name, hire_date, ROW_NUMBER() OVER (ORDER BY hire_date ASC) AS seniority_rank FROM employees;
这个结果里,seniority_rank = 1 的人是最早入职的,也就是司龄最长者。
处理同一天入职员工的并列问题
ROW_NUMBER() 不管值是否重复,都会硬生生分出 1、2、3……比如三人同天入职,它会给三人分别标上 1、2、3,这不符合“司龄相同应并列”的业务逻辑。此时该换 RANK() 或 DENSE_RANK():
-
RANK():同日入职全得 1,下一名跳到 4(产生空位) -
DENSE_RANK():同日入职全得 1,下一名紧接为 2(无空位)
多数 HR 场景倾向用 DENSE_RANK(),写法只需替换函数名:
SELECT name, hire_date, DENSE_RANK() OVER (ORDER BY hire_date ASC) AS seniority_rank FROM employees;
司龄需参与计算时,别在 ORDER BY 里用 DATEDIFF
有人想“先算出司龄天数,再按天数排序”,于是写 ORDER BY DATEDIFF(CURDATE(), hire_date) DESC。这看似合理,但隐患很大:
- 不同数据库的日期函数名不同:
CURDATE()是 MySQL 的,PostgreSQL 得用CURRENT_DATE,SQL Server 是GETDATE() - 如果
hire_date有NULL,DATEDIFF可能返回NULL,导致这部分记录被排在最前或最后,且各库行为不一致 - 纯日期比较更稳定、可索引、无类型转换开销
真正需要显示司龄数值时,单独算一列即可,排序仍坚持用原始 hire_date。
WHERE 条件过滤后排名才有效
如果只查在职员工(status = 'active'),必须先把过滤做完,再套窗口函数。下面这种写法是错的:
-- ❌ 错误:先排名再过滤,会导致排名序号断裂 SELECT * FROM ( SELECT name, hire_date, ROW_NUMBER() OVER (ORDER BY hire_date ASC) r FROM employees ) t WHERE status = 'active';
正确方式是子查询或 CTE 中先过滤,再排名:
-- ✅ 正确:过滤在内层完成 SELECT name, hire_date, ROW_NUMBER() OVER (ORDER BY hire_date ASC) AS seniority_rank FROM employees WHERE status = 'active';
否则你会看到排名中间缺数字,比如在职人员只有 5 个,但排名却是 1、3、4、7、9——因为被筛掉的离职员工占了坑位。
实际用的时候,最容易漏的是过滤时机和同日期的语义选择。业务说“司龄相同算同一级”,就得明确选 DENSE_RANK(),而不是默认套 ROW_NUMBER() 图省事。










