首页 > 数据库 >Oracle SQL子查询与DENSE_RANK去重方法

Oracle SQL子查询与DENSE_RANK去重方法

来源:互联网 2026-06-19 08:41:12

使用DENSE_RANK去重需配合子查询,因为窗口函数不能直接出现在WHERE或GROUPBY中。子查询内分组排名后,外层通过WHERE筛选排名为1的记录。注意DENSE_RANK会保留并列行,若需唯一结果应改用ROW_NUMBER。同时需合理建索引以提升性能。

先说结论。在 Oracle SQL 中使用 DENSE_RANK() 进行去重,标准做法是配合子查询或 CTE。直接写在 WHEREGROUP BY 中会报错,因为窗口函数仅负责生成排名标签,不会实际删除行。真正执行去重操作的是外层查询。

Oracle SQL子查询与DENSE_RANK去重方法

长期稳定更新的攒劲资源: >>>点此立即查看<<<

为何不能直接 WHERE DENSE_RANK() = 1

DENSE_RANK() 是窗口函数,SQL 执行顺序中其生效时机晚于 WHEREGROUP BY。若在 WHERE 中写入 DENSE_RANK() OVER (PARTITION BY id ORDER BY date DESC) = 1,Oracle 会返回 ORA-30483: window functions are not allowed here 错误。

这是新手容易遇到的陷阱——语句看似合理,实际执行时却触发语法错误。问题不在于数据如何排序,而在于 SQL 执行阶段不允许这种写法。

  • 窗口函数只能出现在 SELECT 列表或 ORDER BY 中
  • WHERE、HA VING、GROUP BY 这三个子句均无法直接引用窗口函数的计算结果
  • 若需“按排名过滤结果”,唯一正确做法是将窗口计算放在子查询中,然后在外部通过 WHERE 条件筛选

标准写法:子查询 + WHERE 过滤排名

以登录日志表 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()

DENSE_RANK() 与 ROW_NUMBER() 在去重中的区别

两者均可实现“每组选一条”,但行为差异显著,直接影响数据的确定性。

假设用户 1001 有两条记录,login_time 均为 2026-06-10 09:00:00

  • DENSE_RANK() → 两行均标记 drk = 1,外部 WHERE drk = 1 返回 2 行
  • ROW_NUMBER() → 两行中随机分配(依据 Oracle 内部数据访问顺序)rn = 1rn = 2,外部只返回 1 行

选择准则:

  • 若业务接受并列情况、期望稳定返回多条,使用 DENSE_RANK()
  • 若要求强制单条结果且不关心具体哪一行,使用 ROW_NUMBER()
  • 切勿依赖 ORDER BY 中未明确指定字段的排序稳定性——Oracle 不会保证相同值下的物理顺序

性能与可读性提示

子查询加 DENSE_RANK() 的方式涉及全表扫描与排序,数据量大时性能必然下降。

  • PARTITION BYORDER BY 涉及的字段创建合适索引,例如 (user_id, login_time DESC),可有效缓解性能问题
  • 若仅需获取最新时间(无需整行数据),直接用 GROUP BY + MAX() 效率更高,如 SELECT user_id, MAX(login_time) FROM logins GROUP BY user_id
  • DENSE_RANK() 的真正优势在于需要“完整返回原始字段”的去重场景——例如除时间外还需 ip_addressdevice_type,此时聚合函数无法胜任

这里有一个易被忽略的细节:开发者实际需求往往是“唯一一条结果”,但下意识选择了名称带“dense”的函数。思考一下,ROW_NUMBER() 是否更贴合隐含需求?函数的选择并非单纯语法偏好,而是直接决定了数据的准确性。

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备14008430号-1 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。