views:

1065

answers:

2

Table has surrogate primary key generated from sequence. Unfortunately, this sequence is used for generating keys for some other tables (I did not designed it and I cannot change it).

What is the fastest way to select last n inserted records in Oracle, ordered by id in descending order (last inserted on top)?

n is some relatively small number - number of records to display on page - probably not bigger than 50.

Table now has 30.000.000 records with 10-15 thousands of new records daily.

Database is Oracle 10g.

Edit:
In answer to one comment: This question was motivated with execution plan for query:

  select * from MyTable order by primarykeyfield desc

Execution plan was:

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

I was surprised that Oracle wants to perform full table scan and sorting when it has index on sort field.

Query from accepted answer uses index and avoids sort.

Edit 2:
Re. APC's comment: Sorting was part that surprised me. I expected that Oracle would use index to retrieve rows in expected order. Execution plan for query:

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

uses index instead of full table access and sort (notice condition rownum < 50.000.000 - this is way more than number of records in table and Oracle knows that it should retrieve all records from table). This query returns all rows as first query, but with following execution plan:

| 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)

It was unusual to me that Oracle is creating different execution plans for these two queries that essentially return same result set.

Edit 3: Re Amoq's comment:

Oracle doesn't know that 50M is greater than the number of rows. Sure, it has statistics, but they could be old and wrong - and Oracle would never allow itself to deliver an incorrect result only because the statistics are wrong.

Are you sure? In Oracle versions up to 9 it was recommended to manually refresh statistics from time to time. Since version 10 Oracle automatically updates statistics. What's the use of statistics data if Oracle does not use it for query optimization?

+9  A: 

Use ROWNUM:

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

Note that rownum is applied before sorting for a subquery, that's why you need the two nested queries, otherwise you'll just get n random rows.

Donnie
exactly........
ammoQ
This implies that bork is sortable... Is that compatible with the OP? It very well could be but if so it would be odd that the OP would ask such a simple question.
Mark Canlas
If we take bork to be the sequence-generated primary key the OP mentioned, and assuming it's monotonically increasing, then this query will work.
Dan
See edit for motivation behind this question.
zendar
A: 

Will it be viewed many more times than it is updated? How about keeping another table of the IDs of the last N inserted rows (use a trigger to delete the smallest ID from this table and add a new row with the current-inserted)

-> you now have a table that records the IDs of the last N inserted rows. ANy time you want the N, just join it to the main table. If N changes, pick the max it can be, and then filter it after..

..of course you may find it isnt faster for your app (maintenance of this table may negate any performance gain)

cjard