2016-06-21 2 views
3

Я пытаюсь получить последние 20 строк по времени для пользователей с более чем 100K строк в таблице с более чем 1 миллионом записей. Производительность запроса прекрасна (в мс), когда пользователь имеет небольшое количество записей. Но для получения 20 записей для пользователей с записями 10K-100K требуется более 2 минут.Oracle 11g - top Порядок N медленный

Вот запрос:

select * from (select * from TABLE1                            
     where USER_ID= 41063660 and                               
     COL1=0 and COL2 = 0 
    order by LAST_EVENT_DATE desc) where rownum <= 20 ; 

Существует индекс (I_LASTEVENTDT) на (USER_ID, COL1, COL2, LAST_EVENT_DATE DESC)

Вот объяснить план:

------------------------------------------------------------------------------------------------------------------------ 
| Id | Operation      | Name       | Rows | Bytes |TempSpc| Cost (%CPU)| Time  | 
------------------------------------------------------------------------------------------------------------------------ 
| 0 | SELECT STATEMENT    |        | 20 | 38960 |  | 66959 (1)| 00:13:24 | 
|* 1 | COUNT STOPKEY     |        |  |  |  |   |   | 
| 2 | VIEW       |        | 65500 | 121M|  | 66959 (1)| 00:13:24 | 
|* 3 | SORT ORDER BY STOPKEY  |        | 65500 | 96M| 102M| 66959 (1)| 00:13:24 | 
| 4 |  TABLE ACCESS BY INDEX ROWID| TABLE1      | 65500 | 96M|  | 47280 (1)| 00:09:28 | 
|* 5 |  INDEX RANGE SCAN   | I_LASTEVENTDT     | 65500 |  |  | 309 (0)| 00:00:04 | 
------------------------------------------------------------------------------------------------------------------------ 

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

    1 - filter(ROWNUM<=20) 
    3 - filter(ROWNUM<=20) 
    5 - access("USER_ID"=41063660 AND "COL1"=0 AND 
       "COL2"=0) 

Я попытался следовать примеру, указанному на http://use-the-index-luke.com/sql/sorting-grouping/indexed-order-by

Я пробовал создавая отдельный индекс (USER_ID, COL1, COL2) и (LAST_EVENT_DT DESC), а также попытался индексировать (USER_ID, LAST_EVENT_DT DESC). Производительность была хуже для обоих, хотя для последнего индекса он избавился от порядка сортировки.

Как получить лучшую производительность по этому запросу?

Заранее спасибо.

+0

В плане объяснения говорится, что запрос занял всего 15 секунд для строк 65K (надеюсь, я не ошибаюсь). Почему вы говорите, что требуется 2 минуты? Вы делаете больше обработки до и после запроса? – sstan

+0

План показывает 13 минут, а не 15 секунд и является лишь приблизительной оценкой. Он занимает 2 минуты + в зависимости от количества выбранных строк, когда я запускаю его из своего приложения. – user2755442

+0

К сожалению. Спасибо, что разъяснил это. Только для целей исследования вы попробовали добавить подсказку 'FIRST_ROWS' к вашему запросу? Изменяет ли он план объяснений? – sstan

ответ

0

Сначала попробуйте что-то вроде:

SELECT * 
FROM 
    (SELECT *, ROW_NUMBER() OVER (ORDER BY last_event_date desc) R 
    FROM table1 
    WHERE user_id = 41063660 
    AND col1 = 0 
    AND col2 = 0) 
WHERE R <= 20; 

Если это оказывается не быть быстрыми, попробуйте:

SELECT * 
FROM table1, 
    (SELECT last_event_date, ROW_NUMBER() OVER (ORDER BY last_event_date desc) R 
    FROM table1 
    WHERE user_id = 41063660 
    AND col1 = 0 
    AND col2 = 0) sub 
WHERE table1.user_id = 41063660 
AND table1.col1 = 0 
AND table1.col2 = 0 
AND sub.R = 20 
AND table1.last_event_date >= sub.last_event_date 
AND ROWNUM <= 20; 

Там может быть более прямым путем, чтобы написать его - я не имею Oracle в моем распоряжении, чтобы попробовать.

Альтернативой является реализация функции SQL только для того, чтобы забрать дату 20-го (или N-го) ряда с определенными ключами. Затем вызовите эту функцию SQL в стиле, подобном моему второму примеру, но без подзапроса.

0

Я думаю, вам следует искать индекс быстрого сканирования и не заказывать всю строку. Кроме того, я пытаюсь получить только небольшое количество записей (20), и они снова присоединяются к основному столу.

with Dates as (
    select /*+ Materialize */ LAST_EVENT_DATE 
     from (select LAST_EVENT_DATE 
         from TABLE1                            
        where USER_ID= 41063660 
         and COL1=0 
         and COL2 = 0 
        order by LAST_EVENT_DATE desc) 
     where rownum <= 20) 
select t2.* 
    from (
    select t1.* 
     from TABLE1 t1 join Dates on t1.LAST_EVENT_DATE >= Dates.LAST_EVENT_DATE 
           and t1.USER_ID= 41063660 
           and t1.COL1=0 
           and t1.COL2 = 0 
    order by t1.LAST_EVENT_DATE desc) as t2 
where rownum <= 20; 
Смежные вопросы