应使用ROW_NUMBER()配合CTE或子查询实现分组删除,因其严格保序无重复,可精准控制前N行;RANK()和DENSE_RANK()因并列排名会导致删除行数不可控。

用 ROW_NUMBER() 标记分组内行号再过滤
窗口函数本身不能直接用于 DELETE 语句(MySQL 8.0+、PostgreSQL、SQL Server 等均不支持在 DELETE 中嵌套窗口函数),必须借助子查询或 CTE 把排名结果“固化”出来。最稳妥的做法是先用 ROW_NUMBER() 按分组和排序规则打标,再在外层删掉排名 ≤ N 的行。
常见错误是误用 RANK() 或 DENSE_RANK():它们对相同排序值会给出相同排名,导致实际删除行数不可控;而 ROW_NUMBER() 严格保序、无重复,能精准控制“前 N 行”。
示例(删除每个 category 下销量最高的 2 条记录):
id
- 把内层查询再包一层
SELECT *,让 MySQL 认为它是临时结果集 - 不能依赖别名直接关联,CTE 在 MySQL 8.0+ 虽支持,但 DELETE + CTE 仍受限,优先选派生表
修正写法:
DELETE ... USING
- 想排除
NULL:加WHERE sales IS NOT NULL预过滤 - 想控制
NULL排序位置:PostgreSQL 用ORDER BY sales DESC NULLS LAST;SQL Server / MySQL 可用ORDER BY CASE WHEN sales IS NULL THEN 1 ELSE 0 END, sales DESC
没处理好 NULL,Top N 就可能删错数据——尤其是业务上 NULL 代表“未录入”而非“零销量”时,后果很直接。











