Оракул более быстрый запрос подкачки

У меня есть два запроса подкачки, которые я считаю использовать.

Первый

SELECT * FROM ( SELECT rownum rnum, a.* from (
    select * from members
) a WHERE rownum <= #paging.endRow# ) where rnum > #paging.startRow#

И второе

SELECT * FROM ( SELECT rownum rnum, a.* from (
    select * from members
) a ) WHERE rnum BETWEEN #paging.startRow# AND #paging.endRow#

как вы думаете, какой запрос быстрее?

Ответ 1

Взгляните на планы выполнения, пример с 1000 строк:

SELECT *
  FROM (SELECT ROWNUM rnum
              ,a.*
          FROM (SELECT *
                  FROM members) a
         WHERE ROWNUM <= endrow#)
 WHERE rnum > startrow#;

--------------------------------------------------------------------------------
| Id  | Operation            | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |         |  1000 | 39000 |     3   (0)| 00:00:01 |
|*  1 |  VIEW                |         |  1000 | 39000 |     3   (0)| 00:00:01 |
|   2 |   COUNT              |         |       |       |            |          |
|*  3 |    FILTER            |         |       |       |            |          |
|   4 |     TABLE ACCESS FULL| MEMBERS |  1000 | 26000 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------

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

   1 - filter("RNUM">"STARTROW#")
   3 - filter("MEMBERS"."ENDROW#">=ROWNUM)

И 2.

SELECT *
  FROM (SELECT ROWNUM rnum
              ,a.*
          FROM (SELECT *
                  FROM members) a)
 WHERE rnum BETWEEN startrow# AND endrow#;

-------------------------------------------------------------------------------
| Id  | Operation           | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |         |  1000 | 39000 |     3   (0)| 00:00:01 |
|*  1 |  VIEW               |         |  1000 | 39000 |     3   (0)| 00:00:01 |
|   2 |   COUNT             |         |       |       |            |          |
|   3 |    TABLE ACCESS FULL| MEMBERS |  1000 | 26000 |     3   (0)| 00:00:01 |
-------------------------------------------------------------------------------

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

   1 - filter("RNUM"<="ENDROW#" AND "RNUM">="STARTROW#")

Из этого я бы сказал, что версия 2 может быть немного быстрее, поскольку она включает на один шаг меньше. Но я не знаю о ваших индексах и распределении данных, поэтому вам самим получить эти планы выполнения и судить о ситуации для ваших данных. Или просто протестируйте его.

Ответ 2

На самом деле у меня сейчас нет доступности Oracle, но лучший SQL-запрос для пейджинга наверняка следующий

select *
from (
        select rownum as rn, a.*
        from (
                select *
                from my_table
                order by ....a_unique_criteria...
            ) a
    )
where rownum <= :size
    and rn >  (:page-1)*:size

http://www.oracle.com/technetwork/issue-archive/2006/06-sep/o56asktom-086197.html

Чтобы добиться согласованного разбиения на страницы, вы должны упорядочивать строки с использованием уникальных критериев, что позволит избежать загрузки для страницы X строки, которую вы уже загрузили для страницы Y (! = X).

РЕДАКТИРОВАТЬ:

1) Упорядочить строки с использованием уникальных критериев означает упорядочить данные таким образом, чтобы каждая строка сохраняла одну и ту же позицию при каждом выполнении запроса.

2) Индекс со всеми выражениями, использованными в предложении ORDER BY, поможет быстрее получить результаты, особенно для первых страниц. С этим индексом план выполнения, выбранный оптимизатором, не должен сортировать строки, потому что он будет возвращать строки, прокручивая индекс в его естественном порядке.

3) Между прочим, самый быстрый способ получить результат из запроса - это выполнить запрос только один раз и обработать весь поток со стороны приложения.

Ответ 3

А уже ответил в здесь Но позвольте мне скопировать папку.

Просто хочу обобщить ответы и комментарии. Существует несколько способов сделать разбивку на страницы.

До оракула 12c не было функциональности OFFSET/FETCH, поэтому взгляните на технический документ, как предлагал @jasonk. Это самая полная статья, которую я нашел о различных методах с подробным объяснением преимуществ и недостатков. Это займет много времени, чтобы скопировать их здесь, поэтому я хочу сделать это.

Существует также хорошая статья от создателей jooq, объясняющих некоторые общие оговорки с разбиением на оракулы и другие базы данных. jooq blogpost

Хорошие новости, так как оракул 12c имеет новую функциональность OFFSET/FETCH. Новые возможности OracleMagazine 12c. См. "Самые популярные запросы и разбивка на страницы"

Вы можете проверить версию oracle, выпустив следующую инструкцию

SELECT * FROM V$VERSION