PARTITION BY 必须配合窗口函数使用,不能单独出现;它按指定字段分组计算并保留原行,不聚合数据;常见用途包括部门小计、组内排名等,但不可嵌套使用。

PARTITION BY 不能单独用,必须配合窗口函数
直接写 PARTITION BY 会报错,比如 SELECT name, PARTITION BY dept_id 是非法语法。它只是窗口函数的子句,必须依附于 ROW_NUMBER()、SUM() OVER、AVG() OVER 这类函数才生效。
常见错误是把它当成 GROUP BY 的替代品——但它不聚合行,只“分组计算并保留原行”。想压缩行数还得靠 GROUP BY 或子查询。
-
PARTITION BY dept_id相当于按部门切片,每个部门内独立跑窗口函数 - 和
ORDER BY组合时(如ORDER BY salary DESC),会影响累计值、排名等结果顺序 - 多个字段分区写成
PARTITION BY dept_id, region,注意字段顺序不影响逻辑,但影响可读性
用 SUM() OVER + PARTITION BY 做动态小计
比 GROUP BY + JOIN 更轻量,尤其适合在明细表里加一列“本部门总薪资”这种需求。
SELECT name, dept_id, salary, SUM(salary) OVER (PARTITION BY dept_id) AS dept_total_salary FROM employees;
注意:这里没写 ORDER BY,所以是“部门内所有行的 salary 总和”,不是累计和。如果误加 ORDER BY hire_date,就会变成按入职时间排序后的“到当前行为止的部门累计薪资”,结果完全不同。
- 性能上,大数据量时
SUM() OVER通常比自关联或子查询快,但比 GROUP BY 慢(因不减少行数) - MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持;SQLite 3.25+ 也支持,但旧版不支持
- NULL 值会被忽略在求和中,但分区键为 NULL 会自成一组——这点容易被忽略
RANK() 和 ROW_NUMBER() 在 PARTITION BY 下的行为差异
两者都常用于“每组内排名”,但处理并列的方式不同:
SELECT name, dept_id, salary, RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rnk, ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rn FROM employees;
假设某部门两人同为最高薪 15000:RANK() 给两人都是 1,下一个排 3;ROW_NUMBER() 则强制给 1 和 2(哪怕值相同)。
- 业务上要“并列名次”(如考试分数相同则同为第1名),选
RANK() - 要严格唯一序号(如分页取前 N 条,避免漏掉并列者),用
ROW_NUMBER() -
DENSE_RANK()是折中方案:并列后下一个名次不跳(1,1,2,2,3),但用得少些
嵌套窗口函数?不行,但可以用 CTE 拆解
不能写 SUM(AVG(salary) OVER (...)) OVER (...) —— 窗口函数不能嵌套。想实现“每个部门平均薪资占全公司平均薪资的比例”,得用 CTE 或子查询。
WITH dept_avg AS ( SELECT dept_id, AVG(salary) AS avg_dept_sal FROM employees GROUP BY dept_id ), total_avg AS ( SELECT AVG(salary) AS avg_total_sal FROM employees ) SELECT d.dept_id, d.avg_dept_sal / t.avg_total_sal AS ratio FROM dept_avg d CROSS JOIN total_avg t;
有人试图用双重 OVER 实现,结果要么语法报错,要么逻辑错乱。窗口函数只作用于当前查询层级的行集,没法跨聚合层级“再汇总一次”。
真正复杂的多层统计,优先考虑 CTE 分步算,而不是硬塞进一个 SELECT 里——可读性和调试成本差很多。

















