dense_rank()更适合竞标排序,因其支持“并列不跳号”;需用nulls last或(bid_price is null)显式控制null位置;同价时用submit_time作二级排序不影响名次;按项目排名必须加partition by project_id。

为什么 DENSE_RANK() 比 RANK() 更适合竞标价格排序?
竞标场景中,多个供应商可能报出完全相同的价格,此时需要“并列不跳号”的排名——比如两个 ¥120,000 的报价都排第 2 名,下一个更高价(如 ¥125,000)应排第 3 名,而不是第 4 名。DENSE_RANK() 正是为此设计的;而 RANK() 会在并列后跳过后续名次(两个第 2 名后直接第 4 名),ROW_NUMBER() 则强制唯一编号,完全不符合业务逻辑。
按价格升序排名时如何避免 NULL 干扰?
竞标表里常有未填价格(NULL)的记录,若直接 ORDER BY bid_price,不同数据库对 NULL 的默认排序位置不一致(PostgreSQL 默认排最前,MySQL 可能排最后),会导致 DENSE_RANK() 把 NULL 错误地赋予一个有效名次。
- 显式控制
NULL位置:ORDER BY bid_price ASC NULLS LAST(PostgreSQL/Oracle 支持) - 兼容写法(通用):
ORDER BY (bid_price IS NULL), bid_price—— 先按是否为NULL排(FALSE在前),再按价格升序 - 更稳妥的业务过滤:在
WHERE中提前排除bid_price IS NULL,避免干扰有效排名
如何用 DENSE_RANK() 实现“同价同名次 + 按提交时间保序”?
当多个投标价格相同时,业务上通常要求按提交时间(submit_time)决定展示顺序,但又不能破坏“同价同名次”原则——即名次由价格决定,提交时间只影响同一名次内的排列。
正确做法是把 submit_time 放进 ORDER BY 的第二级,但仅用于稳定排序,不影响名次计算:
SELECT supplier_name, bid_price, submit_time, DENSE_RANK() OVER (ORDER BY bid_price ASC, submit_time ASC) AS rank_by_price FROM bids WHERE bid_price IS NOT NULL;
注意:DENSE_RANK() 只看 ORDER BY 中的**第一个表达式**(这里是 bid_price)是否相等来决定是否并列;后续字段(如 submit_time)仅解决相等时的行间顺序问题,不会导致额外分组。
窗口函数中 PARTITION BY 与竞标分组的关系
如果竞标按项目(project_id)划分,需分别对每个项目的报价独立排名,就必须加 PARTITION BY project_id。漏掉它会导致跨项目混排,比如项目 A 的最低价被排成第 1 名,而项目 B 的高价也挤进同一套全局名次里。
- 错误写法:
DENSE_RANK() OVER (ORDER BY bid_price)→ 全表统一排名 - 正确写法:
DENSE_RANK() OVER (PARTITION BY project_id ORDER BY bid_price) - 若还需按日期分段(如“本周竞标”),可在
WHERE先过滤,或用PARTITION BY project_id, DATE(submit_time)
真正容易被忽略的是:PARTITION BY 的粒度必须和业务认定的“独立竞争单元”完全一致——多一分则割裂合理对比,少一分则混淆不同场景。











