SQL 窗口函数的 PARTITION BY 执行逻辑

PARTITION BY 按指定列分组实现分组内逐行计算,不减少行数;逻辑顺序为先分区、再排序、最后定义窗口帧;NULL值自动归为同一分区,多列分区时含NULL的组合仍混入同一隐式组。

PARTITION BY 的作用是把数据按指定列分组,窗口函数在每个分组内独立计算,不改变原表行数,也不做聚合(除非配合聚合函数使用)。

它不是分组聚合,而是“分组内逐行计算”

PARTITION BY 不会像 GROUP BY 那样压缩行数。原始有多少行,结果还是多少行;只是每行的计算范围被限制在所属分区内部。

  • 例如:COUNT(*) OVER (PARTITION BY dept) 对每个员工返回其所在部门的总人数,每人一行,不是只返回一个数字
  • GROUP BY dept; COUNT(*) 会变成一行一个部门,行数减少

执行顺序上,PARTITION BY 在 ORDER BY 和 ROWS/RANGE 之前生效

窗口函数实际执行时,逻辑顺序是:先按 PARTITION BY 拆出若干子集 → 再在每个子集中按 ORDER BY 排序 → 最后按 ROWS BETWEEN 或 RANGE 定义窗口帧(frame)→ 执行函数计算。

  • 没写 ORDER BY 时,分区内的行顺序不确定(取决于底层扫描顺序),可能导致结果不稳定
  • 没写 ROWS/RANGE 时,默认是 UNBOUNDED PRECEDING TO CURRENT ROW(累计窗口)

NULL 值会被归为同一组

PARTITION BY 列值为 NULL 的所有行,会被划入同一个隐式分区。这和 GROUP BY 中 NULL 被视为相等一致。

  • 如果 dept 是 NULL,所有 dept IS NULL 的记录会组成一个分区,COUNT、SUM 等都在这个 NULL 分区内算
  • 想排除 NULL,可加 WHERE dept IS NOT NULL,或用 CASE 包装分区键(如 PARTITION BY COALESCE(dept, 'UNKNOWN')

可以多列组合分区,语义是“完全匹配才同组”

PARTITION BY dept, region 表示只有 dept 和 region 都相同的行才属于同一分区,等价于联合主键匹配。

  • 注意:多列分区时,任意一列是 NULL,整组比较就为 UNKNOWN,这些行仍会被归进同一个 NULL 组(即 (NULL, NULL)、(NULL, 'A')、('B', NULL) 全部混在一起)
  • 如需严格分离,建议提前用 COALESCE 或 CASE 处理 NULL