postgresql默认nulls first(asc/desc均排最前),sql server无明确默认规则且不支持nulls last,asc时null排最前、desc时排最后;二者order by中若未显式声明null位置,同一语句结果可能错位。

GROUP BY 本身不排序,空值排序行为完全由后续 ORDER BY 决定;SQL Server 和 PostgreSQL 对 NULL 在 ORDER BY 中的默认位置根本不同,不显式声明 NULLS FIRST/LAST 就会出错或结果错位。
ORDER BY 中 NULL 默认排在哪?
PostgreSQL 默认 NULLS FIRST:无论 ASC 还是 DESC,NULL 都排最前。
SQL Server 不支持 NULLS LAST 语法,且无标准默认规则:实际行为是 ASC 时 NULL 排最前、DESC 时排最后——但这个行为未写入文档,纯属引擎隐式实现。
这意味着同一句:
SELECT region, COUNT(*) FROM users GROUP BY region ORDER BY region DESC
在 PostgreSQL 中,region IS NULL 的行会出现在结果最上面;在 SQL Server 中,它们会出现在最下面。业务若依赖“最新区域排前面”,而 region 有大量空值,就直接逻辑错乱。
窗口函数里 NULL 排序更危险
像 ROW_NUMBER() OVER (ORDER BY score DESC) 这类写法,在两库中对 NULL 行的编号可能完全不同:
- PostgreSQL:NULL 被当最大值,
score DESC下它排第一,ROW_NUMBER()标为 1 - SQL Server:NULL 在
DESC下排最后,ROW_NUMBER()可能标为 100+
如果你用这个序号做分页或取 Top N,跨库迁移时数据截断点会偏移,且难以复现。
怎么写才安全?
必须显式控制 NULL 位置,不能靠默认:
- 想让 NULL 靠前:统一写
ORDER BY col DESC NULLS FIRST(PostgreSQL 支持;SQL Server 不识别该语法,需改用ORDER BY CASE WHEN col IS NULL THEN 0 ELSE 1 END DESC, col DESC) - 想让 NULL 靠后:PostgreSQL 写
NULLS LAST;SQL Server 只能用CASE或COALESCE(col, 'zzzz')(字符串)/COALESCE(col, -999999)(数字)兜底 - 聚合场景如
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary):PostgreSQL 自动跳过 NULL;SQL Server 窗口函数也跳过,但若你希望 NULL 参与排序(比如视为最低分),就得提前COALESCE(salary, -1e9)
GROUP BY 分组本身对 NULL 的处理是一致的
这点常被误解:GROUP BY col 时,所有 col IS NULL 的行一定归为同一组,PostgreSQL 和 SQL Server 行为完全一致。问题只出在「分组后怎么排序输出」或「窗口函数内部怎么排序」这两个环节。
最容易被忽略的是:ORDER BY 字段若来自聚合结果(如 COUNT(*)),它不含 NULL,但若来自原始列(如 last_login),而该列又有大量 NULL,那排序行为就彻底失控——尤其当语句从 PostgreSQL 迁到 SQL Server 做 BI 对接时,报表数字看似一样,但“最近活跃用户”列表顺序却完全颠倒。











