使用DENSE_RANK去重需配合子查询,因为窗口函数不能直接出现在WHERE或GROUPBY中。子查询内分组排名后,外层通过WHERE筛选排名为1的记录。注意DENSE_RANK会保留并列行,若需唯一结果应改用ROW_NUMBER。同时需合理建索引以提升性能。
先说结论。在 Oracle SQL 中使用 DENSE_RANK() 进行去重,标准做法是配合子查询或 CTE。直接写在 WHERE 或 GROUP BY 中会报错,因为窗口函数仅负责生成排名标签,不会实际删除行。真正执行去重操作的是外层查询。

长期稳定更新的攒劲资源: >>>点此立即查看<<<
DENSE_RANK() 是窗口函数,SQL 执行顺序中其生效时机晚于 WHERE 和 GROUP BY。若在 WHERE 中写入 DENSE_RANK() OVER (PARTITION BY id ORDER BY date DESC) = 1,Oracle 会返回 ORA-30483: window functions are not allowed here 错误。
这是新手容易遇到的陷阱——语句看似合理,实际执行时却触发语法错误。问题不在于数据如何排序,而在于 SQL 执行阶段不允许这种写法。
以登录日志表 logins 为例,需要为每个 user_id 取最新一条登录记录,按 login_time 降序排列。
SELECT user_id, login_time, ip_address
FROM (
SELECT user_id, login_time, ip_address,
DENSE_RANK() OVER (PARTITION BY user_id ORDER BY login_time DESC) AS drk
FROM logins
)
WHERE drk = 1;
关键点如下:
PARTITION BY user_id 决定每组内部独立排名,不可遗漏ORDER BY login_time DESC 确定排名顺序,升序或降序直接影响最终结果user_id 存在多个完全相同的最大登录时间,DENSE_RANK() 会均为其标记 1,导致“去重”结果非唯一行;若需严格保留一行,应改用 ROW_NUMBER()两者均可实现“每组选一条”,但行为差异显著,直接影响数据的确定性。
假设用户 1001 有两条记录,login_time 均为 2026-06-10 09:00:00:
DENSE_RANK() → 两行均标记 drk = 1,外部 WHERE drk = 1 返回 2 行ROW_NUMBER() → 两行中随机分配(依据 Oracle 内部数据访问顺序)rn = 1 和 rn = 2,外部只返回 1 行选择准则:
DENSE_RANK()ROW_NUMBER()ORDER BY 中未明确指定字段的排序稳定性——Oracle 不会保证相同值下的物理顺序子查询加 DENSE_RANK() 的方式涉及全表扫描与排序,数据量大时性能必然下降。
PARTITION BY 和 ORDER BY 涉及的字段创建合适索引,例如 (user_id, login_time DESC),可有效缓解性能问题GROUP BY + MAX() 效率更高,如 SELECT user_id, MAX(login_time) FROM logins GROUP BY user_idDENSE_RANK() 的真正优势在于需要“完整返回原始字段”的去重场景——例如除时间外还需 ip_address 或 device_type,此时聚合函数无法胜任这里有一个易被忽略的细节:开发者实际需求往往是“唯一一条结果”,但下意识选择了名称带“dense”的函数。思考一下,ROW_NUMBER() 是否更贴合隐含需求?函数的选择并非单纯语法偏好,而是直接决定了数据的准确性。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述