dense_rank()最适合“前三名所有员工”场景,因其并列不跳号(如1,2,2,3),能完整包含并列者;row_number()强制唯一(1,2,3,4)漏掉并列者,rank()跳号(1,2,2,4)导致第3名缺失。

为什么 DENSE_RANK() 比 RANK() 和 ROW_NUMBER() 更适合“前三名所有员工”场景
因为你要的是“薪资排前三的全部人”,不是“前三条记录”。当多人并列第二高薪时,ROW_NUMBER() 会强行分出 2、3、4;RANK() 会跳号(比如 1,2,2,4);而 DENSE_RANK() 是连续不跳的(1,2,2,3),这样用 WHERE rn 才真能捞出所有前三档的人——哪怕第三档有 5 个并列者。
写法要点:必须用子查询或 CTE,不能直接 WHERE DENSE_RANK()
SQL 不允许在 WHERE 子句里直接调用窗口函数,否则报错 Window function is not allowed in WHERE clause。必须先算出排名,再过滤。
推荐用 CTE,清晰且可读性强:
WITH ranked AS (
SELECT name, salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS rn
FROM employees
)
SELECT name, salary
FROM ranked
WHERE rn <h3>容易踩的坑:NULL 值、重复排序键、分区误用</h3><p><code>DENSE_RANK()</code> 默认把 <code>NULL</code> 当作最大值(在 <code>DESC</code> 下排最前),如果薪资字段有空值,它们会挤进“第一名”。需要提前处理:</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/ai/2544" title="Reddo"><img
src="https://img.php.cn/upload/ai_manual/001/246/273/176907432685731.png" alt="Reddo" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/ai/2544" title="Reddo" class="overflowclass">Reddo</a>
<p class="overflowclass">一款AI视频创作工具,主要用于Reddo产品搜索引擎,说出需求,直达产品/游戏/工具/公司/工作/文章/视频/商品,适合需要提升相关任务效率的用户。</p>
</div>
<a rel="nofollow" href="/ai/2544" title="Reddo" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
- 加
WHERE salary IS NOT NULL过滤掉空薪员工 - 或改用
ORDER BY salary DESC NULLS LAST(PostgreSQL/Oracle 支持;MySQL 8.0+ 不支持该语法,得靠WHERE)
另外,如果误加了 PARTITION BY department,结果就变成“每个部门前三”,而不是全公司前三——确认业务需求再决定是否分区。
性能注意:ORDER BY 字段必须有索引
DENSE_RANK() OVER (ORDER BY salary DESC) 在大数据量下会触发全表扫描+排序,非常慢。确保 salary 列上有索引:
CREATE INDEX idx_emp_salary_desc ON employees(salary DESC);
某些旧版 MySQL(如 5.7)不支持降序索引,那就建普通索引 (salary),优化器仍可能有效利用;但 PostgreSQL / MySQL 8.0+ / SQL Server 都建议显式声明 DESC。
真正麻烦的是当排序依据是表达式(比如 salary * 12)或涉及多列(ORDER BY dept_id, salary DESC)时,索引设计就得更谨慎——这时候别只盯着函数怎么写,先看执行计划里有没有用上索引。










