Oracle: оптимизация OrderBy для First/Single[OrDefault], Tak
От: AK107  
Дата: 03.08.15 18:11
Оценка:
Имеется проблема со скоростью выполнения сабжа на больших выборках вследствие генерации слишком обобщенного/неоптимального sql-запроса. Смысл в том, что для OrderBy генерируется лишний select пронумеровывающий выбираемое множество, который тормозит на больших выборках из которых нужно получить 1-2-10 первых строк.

речь идет о тройке: First[OrDefault], Single[OrDefault] и Take (без Skip выше по иерархии).

сейчас генерируется такой запрос для GetTable<Table>().FirstOrDefault:
SELECT t2.*
FROM
(
        SELECT t.*, ROWNUM as rn
        FROM
        (
                SELECT
                        t1.*
                FROM
                        t_table t1
                ORDER BY
                        t1.value
        ) t
) t2
WHERE
        t2.rn <= 1


где выделенное лишнее, а достаточно такого:

SELECT * FROM
(
                SELECT
                        t1.*
                FROM
                        t_table t1
                ORDER BY
                        t1.value
)
WHERE ROWNUM <= 1


на маленьких выборках незаметно, но на больших разница на порядки: для примера на выборке в 100к строк первый вариант работает (с индексом) за пол секунды, а второй за сотые доли секунды, не говоря о нагрузке на саму БД!

з.ы. почему упомянул Take — потому, что если в запрос не используется Skip, то так же нет необходимости во внутреннем запросе пронумеровывающем сортированное множество.

з.ы. добавлю также, что в идеале можно залепить еще и хинт FIRST_ROWS(n) на внешний select где идет использование rownum примерно так:
SELECT /*+ FIRST_ROWS(1) */ * FROM
(
                SELECT
                        t1.*
                FROM
                        t_table t1
                ORDER BY
                        t1.value
)
WHERE ROWNUM <= 1


что касается Take вместе со Skip (Pagination), то вот у Тома можно подсмотреть как наиболее красиво/правильно/оптимально запилить:
select * 
  from ( select /*+ FIRST_ROWS(n) */ 
  a.*, ROWNUM rnum 
      from ( your_query_goes_here, 
      with order by ) a 
      where ROWNUM <= 
      :MAX_ROW_TO_FETCH ) 
where rnum  >= :MIN_ROW_TO_FETCH;
Отредактировано 03.08.2015 18:50 AK107 . Предыдущая версия . Еще …
Отредактировано 03.08.2015 18:31 AK107 . Предыдущая версия .
Отредактировано 03.08.2015 18:12 AK107 . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.