2009-12-01 67 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不夠聰明,以優化這種類型。所以這可能不是生產使用的好主意。