【问题标题】:Oracle TOP N ordered rowsOracle TOP N 有序行
【发布时间】:2018-12-28 05:29:30
【问题描述】:

我想从按日期排序的 Oracle 表中获取前 N 行。

执行此操作的常用方法,并且此解决方案会针对我在 SO/google 上找到的每个问题返回。

Select *
from
(select * from
myTable 
ordered by Date desc)
where rownum < N

这个解决方案在我的情况下是不切实际的,因为 myTable 包含大量行 导致 Oracle 花费太长时间返回子查询中的所有行。

问题是,有没有办法限制子查询中返回的 ORDERED 行数?

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    您推断 Oracle 必须在过滤掉第一个 N 之前返回子查询中的所有行是错误的。它将开始从子查询中获取行,并在返回 N 行时停止

    话虽如此,可能是 Oracle 需要从表中选择所有行并对其进行排序,然后才能开始返回它们。但如果在 ORDER BY 子句中使用的列上有索引,则可能没有。

    Oracle 与任何其他 DBMS 处于相同的位置:如果您有一个大表,在您排序的列上没有索引,它怎么可能知道哪些行是前 N 行而不首先获取所有行并进行排序他们?

    【讨论】:

      【解决方案2】:

      问题是,有没有办法限制 ORDERED 行的数量 在子查询中返回?

      以下是我通常用于 top-n 类型查询(在本例中为分页查询):

      select * from (
        select a.*, rownum r
        from (
          select *
          from your_table
          where ...
          order by ...
        ) a
        where rownum <= :upperBound
      )
      where r >= :lowerBound;
      

      我通常在内部查询中使用索引列进行排序,使用rownum意味着Oracle可以使用count(stopkey)优化。所以,不一定要做全表扫描:

      create table t3 as select * from all_objects;
      alter table t3 add constraint t_pk primary key(object_id);
      analyze table t3 compute statistics;
      
      delete from plan_table;
      commit;
      explain plan for
      select * from (
        select a.*, rownum r
        from (
          select object_id, object_name
          from t3
          order by object_id
        ) a
        where rownum <= 2000
      )
      where r >= 1;
      
      select operation, options, object_name, id, parent_id, position, cost, cardinality, other_tag, optimizer
      from plan_table
      order by id;
      

      您会发现 Oracle 使用 t_pk 进行全索引扫描。还要注意 stopkey 选项的使用。

      希望能解释我的回答;)

      【讨论】:

      • 您应该解释为什么您认为这是该问题的合适答案。
      【解决方案3】:

      如果您有大量数据,Order by 可能会成为繁重的操作。看看你的执行计划。如果数据不是实时的,您可以在这些选择上创建材质视图...

      【讨论】:

        【解决方案4】:

        在旧版本的 ORACLE (8.0) 中,您无法在子查询中使用 ORDER BY 子句。 所以,只有我们这些还用过一些古老版本的人,还有另一种处理方法:UNION 运算符的魔力。 UNION 将按查询中的列对记录进行排序:

        例子:

        SELECT * FROM
        (SELECT EMP_NO, EMP_NAME FROM EMP_TABLE
        UNION
        SELECT 99999999999,'' FROM DUAL)
        WHERE ROWNUM<=5
        where 99999999999 is bigger then all values in EMP_NO;
        

        或者,如果您想选择工资最高的 5 名员工:

        SELECT EMP_NO, EMP_NAME, 99999999999999-TMP_EMP_SAL
        FROM
        (SELECT 99999999999999-EMP_SAL TMP_EMP_SAL, EMP_NO, EMP_NAME 
        FROM EMP_TABLE
        UNION
        SELECT 99999999999999,0,'' FROM DUAL)
        WHERE ROWNUM<=5;
        

        问候,

        维吉尔·约内斯库

        【讨论】:

          猜你喜欢
          • 2017-01-01
          • 2011-09-01
          • 2016-10-23
          • 2014-09-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多