在表中选择`n`最后插入的记录 - oracle

时间:2009-12-01 18:23:38

标签: sql oracle oracle10g

表具有从序列生成的代理主键。不幸的是,这个序列用于为其他一些表生成密钥(我没有设计它,我也无法改变它)。

在Oracle中选择最后n个插入记录的最快方法是什么,按ID降序排列(最后插入到顶部)?

n是一些相对较小的数字 - 页面上显示的记录数量 - 可能不超过50个。

表现在有30.000.000条记录,每天有10-15,000条新记录。

数据库是Oracle 10g。

修改
回答一条评论:这个问题的动机是查询的执行计划:

  select * from MyTable order by primarykeyfield desc

执行计划是:

--------------------------------------------- 
| Id  | Operation          | Name        |     
---------------------------------------------  
|   0 | SELECT STATEMENT   |             |
|   1 |  SORT ORDER BY     |             |
|   2 |   TABLE ACCESS FULL| MyTable     |
---------------------------------------------  

我很惊讶Oracle希望在排序字段上有索引时执行全表扫描和排序。

来自已接受答案的查询使用索引并避免排序。

编辑2:
回覆。 APC的评论:排序是让我感到惊讶的一部分。我预计Oracle会使用index来按预期顺序检索行。查询的执行计划:

select * from (select * from arh_promjene order by promjena_id desc) x 
   where rownum < 50000000

使用索引而不是全表访问和排序(通知条件rownum < 50.000.000 - 这比表中的记录数量多,Oracle知道它应该从表中检索所有记录)。此查询将所有行作为第一个查询返回,但具有以下执行计划:

| Id  | Operation                     | Name         | 
-------------------------------------------------------
|   0 | SELECT STATEMENT              |              | 
|*  1 |  COUNT STOPKEY                |              | 
|   2 |   VIEW                        |              | 
|   3 |    TABLE ACCESS BY INDEX ROWID| MyTable      | 
|   4 |     INDEX FULL SCAN DESCENDING| SYS_C008809  | 

Predicate Information (identified by operation id):    
---------------------------------------------------    

   1 - filter(ROWNUM<50000000)                         

对我而言,Oracle正在为这两个基本上返回相同结果集的查询创建不同的执行计划。

编辑3: Re Amoq的评论:

  

Oracle不知道50M是   大于行数。当然,   它有统计数据,但它们可能是   古老而错误 - 而甲骨文永远不会   允许自己传递错误   结果只是因为统计数据   错。

你确定吗?在最多9个Oracle版本中,建议不时手动刷新统计信息。从版本10开始,Oracle会自动更新统计信如果Oracle不将其用于查询优化,那么统计数据的用途是什么?

5 个答案:

答案 0 :(得分:15)

使用ROWNUM

select
  *
from
  (
    select
      *
    from
      foo
    order by
      bork
   ) x
where
  ROWNUM <= n

请注意,{<1}}在排序子查询之前应用,这就是您需要两个嵌套查询的原因,否则您只会得到rownum个随机行。

答案 1 :(得分:4)

它会被更多次查看而不是更新吗?如何保留最后N个插入行的ID的另一个表(使用触发器从该表中删除最小的ID并添加一个当前插入的新行)。

您现在有一个表记录了最后N个插入行的ID。任何时候你想要N,只需将它加入主表。如果N改变,选择它可以的最大值,然后在...之后过滤它...当然你可能发现你的应用程序不是那么快(维护这个表可能会抵消任何性能提升)

答案 2 :(得分:4)

如果您没有严格增加的字段,您还可以使用ORA_ROWSCN(系统更改编号)作为其近似值。

select * from (select * from student order by ORA_ROWSCN desc) where rownum<10

警告:这不准确,因为Oracle每个块只记录一个SCN,而不是每行。它似乎也做了全表扫描 - 可能oracle不够聪明,无法优化这种类型。因此,这可能不适合生产使用。

答案 3 :(得分:3)

如果您不知道字段名称或表名称以外的任何内容,这可能对您有帮助。

select * from (
  select * from(
    select rownum r,student.* from student where rownum<=(
      select max(rownum) from student
    )
  ) order by r desc
 ) where r<=10;

答案 4 :(得分:3)

尝试执行index_desc提示

select /*+ index_desc(MyTable,<PK_index>) */ * from MyTable order by primarykeyfield desc