Пейджинг з Oracle


97

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

Я роблю це в .NET з C #, думаючи, що це не важливо, якщо я зможу це зрозуміти зі сторони sql, то я повинен бути крутим.

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

Відповіді:


144

Щось подібне мало би спрацювати: З блогу Франса Буми

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 рядків, застосовується порядок сортування, а потім кожному рядку присвоюється рядок. Зовнішній вибір (и) використовує ці номери рядків для пошуку "сторінки", яку ви шукаєте, на основі вашого розміру сторінки.
Brian Schmitt

9
Це приємно, але жахливо повільно для великих виділень, просто перевірте, який час буде вибрати від 0 до 1000 та 500 000 до 501 000 ... Я використовував такий тип виділеної структури, зараз я шукаю обхідний шлях.
newhouse

3
@ n3whous3 ви можете спробувати це - inf.unideb.hu/~gabora/pagination/results.html
jasonk

7
Я здивувався, чому два WHEREне можна поєднати AND, а потім знайшов це: orafaq.com/wiki/ROWNUM
Гао

1
Пагінація Oracle руйнує мій день.
Ефір

134

Запитайте Тома про пагінацію та дуже, дуже корисні аналітичні функції.

Це уривок із цієї сторінки:

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 Ви маєте рацію. На цій сторінці його важко знайти. Витяг додано.
Chobicus

Це правильна відповідь, якщо ви хочете динамічно змінювати своє замовлення.
chakeda

74

В інтересах повноти, для людей, які шукають більш сучасне рішення, в 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,993 секунди / 256

Короткий зміст: використовуйте обережно, доки Oracle не покращить цю процедуру. Якщо у вас є індекс, з яким можна працювати, можливо, вам вдасться піти за допомогою нового методу.

Сподіваюся, у мене найближчим часом буде копія 18c, з якою можна пограти, і я можу її оновити


Це чудова відповідь для користувачів 12c
Lalji Gajera

1
Синтаксис чистіший, але продуктивність гірша ( dba-presents.com/index.php/databases/oracle/… )
wweicker

Приємно знати, дякую @wweicker. Сподіваємось, продуктивність скоро буде виправлена ​​Oracle; хоча, знаючи Oracle, це може бути далекою надією!
JoelC

Синтаксис є новим, і він перетворюється на звичайні виклики ROW_NUMBER / RANK. Пов’язане Як обмежити кількість рядків, що повертаються запитом Oracle після замовлення?
Лукаш

@JoelC чи були якісь зміни у вашій думці?
Райан

11

Просто хочу узагальнити відповіді та коментарі. Існує ряд способів зробити пагінацію.

Чи не До оракула 12с були OFFSET / FETCH функціональності, тому зверніть увагу на офіційному документі , як це було запропоновано @jasonk. Це найповніша стаття, яку я знайшов про різні методи з детальним поясненням переваг та недоліків. Щоб скопіювати їх сюди, знадобиться значна кількість часу, тому я не буду цього робити.

Також є хороша стаття від творців jooq, яка пояснює деякі загальні застереження щодо пагінації oracle та інших баз даних. допис у блозі jooq

Хороші новини, оскільки з Oracle 12c ми отримали нову функціональність OFFSET / FETCH. Нові можливості OracleMagazine 12c . Будь ласка, зверніться до "Найпопулярніших запитів та пагінації"

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

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]


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;
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.