Выберите `n` последних вставленных записей в таблице - оракул - PullRequest
6 голосов
/ 01 декабря 2009

Таблица имеет суррогатный первичный ключ, сгенерированный из последовательности. К сожалению, эта последовательность используется для генерации ключей для некоторых других таблиц (я не разработал ее и не могу ее изменить).

Какой самый быстрый способ выбрать последние n вставленные записи в Oracle, упорядоченные по id в порядке убывания (последний вставленный сверху)?

n - это относительно небольшое число - количество записей, отображаемых на странице, - вероятно, не больше 50.

В таблице теперь 30 000 000 записей с 10-15 тысячами новых записей ежедневно.

База данных 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 будет использовать индекс для извлечения строк в ожидаемом порядке. План выполнения запроса:

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's комментарий:

Oracle не знает, что 50M больше, чем количество строк. Конечно, у него есть статистика, но они могут быть старый и неправильный - и Oracle никогда бы не позволить себе поставить неверный результат только потому, что статистика неправильно.

Вы уверены? В версиях Oracle до 9 рекомендуется время от времени обновлять статистику вручную. Начиная с версии 10 Oracle автоматически обновляет статистику. Какая польза от статистических данных, если Oracle не использует их для оптимизации запросов?

Ответы [ 5 ]

15 голосов
/ 01 декабря 2009

Использование ROWNUM:

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

Обратите внимание, что rownum применяется перед сортировкой для подзапроса, поэтому вам нужны два вложенных запроса, в противном случае вы просто получите n случайных строк.

4 голосов
/ 21 марта 2014

В тех случаях, когда у вас нет строго увеличивающегося поля, вы также можете использовать ORA_ROWSCN (номер изменения системы) в качестве приблизительного значения.

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

Внимание: это не точно, поскольку Oracle записывает только один SCN на блок, а не на строку. Также кажется, что выполняется полное сканирование таблицы - возможно, oracle недостаточно умен, чтобы оптимизировать этот вид. Так что это не может быть хорошей идеей для производственного использования.

4 голосов
/ 28 апреля 2010

Будет ли оно просматриваться намного чаще, чем обновляется? Как насчет сохранения другой таблицы идентификаторов последних N вставленных строк (используйте триггер, чтобы удалить наименьший идентификатор из этой таблицы и добавить новую строку с текущей вставкой).

Теперь у вас есть таблица, в которой записаны идентификаторы последних N вставленных строк. В любое время, когда вы хотите N, просто присоедините его к основному столу. Если N изменяется, выберите максимальное значение, которое может быть, а затем отфильтруйте его после ... конечно, вы можете обнаружить, что оно не так быстро для вашего приложения (ведение этой таблицы может свести на нет любое повышение производительности)

3 голосов
/ 17 января 2014

Попробуйте выполнить подсказку index_desc

select /*+ index_desc(MyTable,<PK_index>) */ * from MyTable order by primarykeyfield desc
3 голосов
/ 07 февраля 2013

Это может помочь вам, если вы не знаете названия полей или чего-либо, кроме имени таблицы ....

select * from (
  select * from(
    select rownum r,student.* from student where rownum<=(
      select max(rownum) from student
    )
  ) order by r desc
 ) where r<=10;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...