Пейджинг с Oracle

97

Я не так хорошо знаком с Oracle, как хотелось бы. У меня около 250 тыс. Записей, и я хочу отображать их по 100 на странице. В настоящее время у меня есть одна хранимая процедура, которая извлекает все четверть миллиона записей в набор данных с помощью адаптера данных, набора данных и метода dataadapter.Fill (набор данных) для результатов из хранимой процедуры. Если у меня есть целочисленные значения «Номер страницы» и «Количество записей на странице», которые я могу передать в качестве параметров, что было бы наилучшим способом вернуть только этот конкретный раздел. Скажем, если я передам 10 в качестве номера страницы и 120 в качестве количества страниц, из оператора select я получу от 1880-го до 1200-го или что-то в этом роде, моя математика в моей голове может быть отключена.

Я делаю это в .NET с C #, подумал, что это не важно, если я смогу понять это правильно на стороне sql, тогда я должен быть крутым.

Обновление: я смог использовать предложение Брайана, и оно отлично работает. Я хотел бы поработать над некоторой оптимизацией, но страницы появляются через 4–5 секунд, а не за минуту, и мой элемент управления подкачкой смог очень хорошо интегрироваться с моими новыми сохраненными процессами.

Стивенбайер
источник

Ответы:

147

Примерно так должно работать: Из блога Франса Боумы

SELECT * FROM
(
    SELECT a.*, rownum r__
    FROM
    (
        SELECT * FROM ORDERS WHERE CustomerID LIKE 'A%'
        ORDER BY OrderDate DESC, ShippingDate DESC
    ) a
    WHERE rownum < ((pageNumber * pageSize) + 1 )
)
WHERE r__ >= (((pageNumber-1) * pageSize) + 1)
Брайан Шмитт
источник
4
Да, это «встроенный» столбец, который поддерживает Oracle, он всегда начинается с 1 и увеличивается для каждой строки. Итак, в этом фрагменте кода, если у вас 1000 строк, применяется порядок сортировки, а затем каждой строке назначается rownum. Внешний выбор (ы) использует эти номера строк для поиска «страницы», которую вы ищете, в зависимости от размера вашей страницы.
Брайан Шмитт
9
Это хорошо, но ужасно медленно для больших выборок, просто проверьте, сколько времени будет для выбора от 0 до 1000 и от 500 000 до 501 000 ... Я использовал такую ​​структуру выбора, теперь я ищу обходной путь.
Newhouse 06
3
@ n3whous3, вы можете попробовать это - inf.unideb.hu/~gabora/pagination/results.html
jasonk
7
Я задавался вопросом, почему два WHEREне могут быть объединены AND, и затем нашел это: orafaq.com/wiki/ROWNUM
Mengdi Gao
1
Пагинация Oracle портит мне день.
Aetherus 01
135

Спросите Тома о разбиении на страницы и очень-очень полезных аналитических функциях.

Это выдержка с той страницы:

select * from (
    select /*+ first_rows(25) */
     object_id,object_name,
     row_number() over
    (order by object_id) rn
        from all_objects)
    where rn between :n and :m
        order by rn;
Чобикус
источник
7
На самом деле это гораздо лучшая реализация, хотя ее трудно найти в этом посте. Когда у вас много больших страниц, другой ответ также должен проходить по всем строкам с предыдущих страниц. В сложных запросах это означает, что более поздние страницы работают хуже, чем предыдущие.
tallseth
@tallseth Вы правы. На этой странице его сложно найти. Добавлен отрывок.
Chobicus
Это правильный ответ, если вы хотите динамически изменять свой заказ.
chakeda
75

В интересах полноты, для людей, ищущих более современное решение, в Oracle 12c есть некоторые новые функции, включая улучшенную подкачку страниц и верхнюю обработку.

Пейджинг

Пейджинг выглядит так:

SELECT *
FROM user
ORDER BY first_name
OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY;

Лучшие записи N

Получение топовых записей выглядит так:

SELECT *
FROM user
ORDER BY first_name
FETCH FIRST 5 ROWS ONLY

Обратите внимание, как в обоих приведенных выше примерах запросов есть ORDER BYпредложения. Новые команды соблюдают их и запускаются на отсортированных данных.

Мне не удалось найти хорошую справочную страницу Oracle для FETCHили, OFFSETно на этой странице есть отличный обзор этих новых функций.

Производительность

Как отмечает @wweicker в комментариях ниже, производительность - это проблема с новым синтаксисом в 12c. У меня не было копии 18c, чтобы проверить, улучшил ли ее Oracle с тех пор.

Интересно, что мои фактические результаты были возвращены немного быстрее, когда я впервые выполнил запросы к своей таблице (более 113 миллионов строк) для нового метода:

  • Новый метод: 0,013 секунды.
  • Старый метод: 0,107 секунды.

Однако, как упомянул @wweicker, план объяснения выглядит намного хуже для нового метода:

  • Стоимость нового метода: 300 110
  • Стоимость старого метода: 30

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

Давайте посмотрим, включает ли один неиндексированный столбец в предыдущий набор данных:

  • Время / стоимость нового метода: 189,55 секунды / 998 908
  • Время / стоимость старого метода: 1,973 секунды / 256

Резюме: используйте с осторожностью, пока Oracle не улучшит эту обработку. Если у вас есть индекс, с которым можно работать, возможно, вам удастся использовать новый метод.

Надеюсь, у меня скоро будет копия 18c, с которой можно будет поиграть, и я смогу обновить

JoelC
источник
Это отличный ответ для пользователей 12c
Лалджи Гаджера
1
Синтаксис чище, но производительность хуже ( dba-presents.com/index.php/databases/oracle/… )
wweicker
Полезно знать, спасибо @wweicker. Надеюсь, Oracle скоро исправит производительность; хотя, зная Oracle, это может быть далекой надеждой!
JoelC
Синтаксис новый, он преобразован в обычные вызовы ROW_NUMBER / RANK. Связанный Как мне ограничить количество строк, возвращаемых запросом Oracle после заказа?
Lukasz Szozda
@JoelC изменилось ли ваше мнение?
Райан
11

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

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

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

Хорошие новости, начиная с oracle 12c, у нас появилась новая функциональность OFFSET / FETCH. Новые возможности OracleMagazine 12c . Пожалуйста, обратитесь к разделу «Топ-N запросов и разбивки на страницы».

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

SELECT * FROM V$VERSION
Вадим Кирильчук
источник
7

Попробуйте следующее:

SELECT *
FROM
  (SELECT FIELDA,
    FIELDB,
    FIELDC,
    ROW_NUMBER() OVER (ORDER BY FIELDC) R
  FROM TABLE_NAME
  WHERE FIELDA = 10
  )
WHERE R >= 10
AND R   <= 15;

через [tecnicume]

Furetto
источник
0

В своем проекте я использовал Oracle 12c и java . Код подкачки выглядит так:

 public public List<Map<String, Object>> getAllProductOfferWithPagination(int pageNo, int pageElementSize, Long productOfferId, String productOfferName) {
    try {

        if(pageNo==1){
            //do nothing
        } else{
            pageNo=(pageNo-1)*pageElementSize+1;
        }
        System.out.println("algo pageNo: " + pageNo +"  pageElementSize: "+ pageElementSize+"  productOfferId: "+ productOfferId+"  productOfferName: "+ productOfferName);

        String sql = "SELECT * FROM ( SELECT * FROM product_offer po WHERE po.deleted=0 AND (po.product_offer_id=? OR po.product_offer_name LIKE ? )" +
             " ORDER BY po.PRODUCT_OFFER_ID asc) foo OFFSET ? ROWS FETCH NEXT ? ROWS ONLY ";

       return jdbcTemplate.queryForList(sql,new Object[] {productOfferId,"%"+productOfferName+"%",pageNo-1, pageElementSize});

    } catch (Exception e) {
        System.out.println(e);
        e.printStackTrace();
        return null;
    }
Фердус Вахид
источник