应使用DENSE_RANK() OVER (PARTITION BY job_id ORDER BY matching_score DESC)实现同分同名次且不跳号的岗位内匹配度排名,必须配合PARTITION BY按岗位分组,匹配度字段需预计算并建立索引以保障性能。

窗口函数怎么给求职者按匹配度排序
直接用 ROW_NUMBER()、RANK() 或 DENSE_RANK() 就能对同一岗位下的求职者按匹配度打排名,但必须配合 PARTITION BY 按岗位分组,否则全表排成一列就失去业务意义。匹配度字段通常是预计算好的数值(比如简历关键词重合率、技能加权得分),不能在窗口函数里实时算模糊匹配——那会拖慢查询且无法索引加速。
常见错误是漏写 PARTITION BY job_id,结果所有求职者被混在一起排名,HR看到“张三在全部候选人中排第2”毫无参考价值;另一个坑是用了 ROW_NUMBER() 但业务上要求同分同名次,这时得换 RANK(),否则两个95分的候选人会被强行拆成第1和第2名。
-
RANK():相同匹配度并列,跳过后续名次(95,95,87 → 1,1,3) -
DENSE_RANK():相同匹配度并列,不跳名次(95,95,87 → 1,1,2) - 排序字段建议用
matching_score DESC,避免升序导致高分排末尾
匹配度分数怎么从简历字段动态生成
窗口函数本身不负责算匹配度,它只排序。真正要动脑的是怎么把非结构化简历转成可比数字。别在 SELECT 里写一堆 CASE WHEN 去匹配“Java”“Spring”“分布式”,效率低还难维护。推荐提前在 ETL 阶段或物化视图里固化:skills_weighted_score(技能权重和)、years_of_experience(年限校准值)、edu_match(学历门槛达标为1否则0)——这些字段存进候选人快照表,窗口函数只读它们。
如果真要临时算,至少用 ts_vector + ts_rank(PostgreSQL)或 CONTAINS(SQL Server)这类带索引支持的文本匹配,而不是 LIKE '%Java%' 全表扫描。实测某招聘库用 LIKE 算匹配度,10万简历查一个岗位平均 8.2 秒;换成预计算字段后压到 120ms 内。
- 避免在
ORDER BY子句里调用 UDF 或复杂表达式,窗口函数会反复执行 - MySQL 8.0+ 支持窗口函数,但不支持
ts_rank,得靠应用层或前置计算 - 匹配度字段务必设为
NOT NULL,否则NULL候选人会排最前或最后,取决于数据库默认行为
如何让HR看到“本岗位前3名”而不暴露全部数据
直接 WHERE rank 会报错——窗口函数不能在 <code>WHERE 里引用。必须套一层子查询或 CTE。CTE 更清晰,也方便后续加筛选(比如只看已投递未拒信的候选人)。
WITH ranked_candidates AS (
SELECT
candidate_id,
job_id,
matching_score,
RANK() OVER (PARTITION BY job_id ORDER BY matching_score DESC) AS rank_in_job
FROM candidate_job_scores
WHERE status = 'applied'
)
SELECT candidate_id, job_id, matching_score
FROM ranked_candidates
WHERE rank_in_job <= 3;
注意 status = 'applied' 要写在 CTE 里过滤,不是外面,否则窗口函数会先算全量再裁剪,浪费资源。另外,如果岗位只有2个候选人,RANK() 排出 1,1,rank_in_job 仍只返回这2个——这点没问题;但若用 <code>ROW_NUMBER() 就可能漏掉并列第2名。
为什么在分页场景下 RANK() 比 ROW_NUMBER() 更稳
招聘系统常要“加载更多匹配者”,前端传 page=2&size=10,后端用 OFFSET/LIMIT。如果用 ROW_NUMBER(),第1页取 1–10,第2页取 11–20,但实际业务中第1页末尾两个都是匹配度92分,第2页开头那个也是92分——人为割裂了并列群体。HR会觉得“咦,怎么中间漏了一个同样高的?”
用 RANK() 后,你可以先查出所有 rank_in_job 的记录,再在应用层按需切片,或者用 <code>WHERE rank_in_job BETWEEN 11 AND 20 ——这样保证同分必同页,逻辑更符合人工决策习惯。代价是可能某页返回少于10条(比如第2页只有7人并列第11名),但总比漏人强。
- 别依赖数据库自动分页,尤其当匹配度分布不均时(大量85–88分扎堆)
- PostgreSQL 的
OFFSET在大偏移量下性能差,用游标分页(WHERE rank_in_job > last_seen_rank)更靠谱 - MySQL 8.0 的窗口函数不支持在子查询里嵌套窗口,CTE 是唯一安全写法

















