2009-12-01 106 views
6

表具有从序列生成的替代主键。不幸的是,这个序列用于生成一些其他表的键(我没有设计它,我不能改变它)。在表中选择`n`最后插入记录 - oracle

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

n是一些比较小的数 - 要在页面上显示的记录数 - 可能不大于50

表现拥有30.000.000记录10-15数以千计的新记录每天。

数据库是Oracle 10g。

编辑:
在回答一个评论:这个问题与执行计划的动机查询:

select * from MyTable order by primarykeyfield desc 

执行计划是:

​​

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

从接受的答案查询使用索引,并避免排序。

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

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)       

这是不寻常的,我认为甲骨文正在为这两个查询,基本上返回相同的结果集不同的执行计划。

编辑3: 重新Amoq的评论:

Oracle不知道50M比行数更大 。当然, 它有统计数据,但它们可能是 旧的和错误的 - 并且Oracle从不会 允许自己发送错误的 结果,只是因为统计信息是 错误。

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

+0

为什么不幸的是,序列也用于其他表?查询检索最后n个插入的记录并不重要,因为一个序列永远不会被Oracle无条件地取消。所以你不能在max-n和max之间做一个简单的id。 – tuinstoel 2009-12-01 19:00:06

+0

您的查询为* all *行选择* all *列。为什么你对Oracle进行全面的表扫描感到惊讶?否则它会如何获得数据以满足该查询? – APC 2009-12-02 11:11:31

+0

Oracle不知道* 50M是否大于行数。当然,它有统计数据,但它们可能是老的和错误的 - 并且Oracle只会因为统计数据错误而不会允许自己提供不正确的结果。 – 2009-12-02 12:19:25

回答

15

使用ROWNUM

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

注意rownum被之前应用排序的子查询,这就是为什么你需要两个嵌套查询,否则你会只得到n随机行。

+0

究竟是........ – 2009-12-01 18:30:52

+0

这意味着bork是可排序的...是否与OP兼容?这很好,但如果是这样的话,OP会问这样一个简单的问题会很奇怪。 – 2009-12-01 18:55:15

+1

如果我们把bork作为序列生成的主键,OP提到了,假设它是单调递增的,那么这个查询就可以工作。 – Dan 2009-12-01 19:41:38

4

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

你现在有记录的最后N的ID插入行的表。任何时候你想要N,只需将它加入主表。如果N变化,挑最大也可以是,然后后使其过滤......当然,你可能会发现它不那么你的应用程序快速(此表的维护可以否定任何性能增益)

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; 
3

尝试做一个index_desc提示

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

在情况下,你没有一个严格的越来越多的领域,您还可以使用ORA_ROWSCN(系统更改编号)作为近似值秒。

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

注意:这不是确切的,因为Oracle每块只记录一个SCN,而不是每行记录一个SCN。此外,它似乎做了一个全表扫描 - 可能oracle不够聪明,以优化这种类型。所以这可能不是生产使用的好主意。