Paging con Oracle


97

Non ho familiarità con Oracle come vorrei. Ho circa 250.000 record e desidero visualizzarli 100 per pagina. Attualmente ho una procedura memorizzata che recupera tutto il quarto di milione di record in un set di dati utilizzando un adattatore dati, un set di dati e il metodo dataadapter.Fill (set di dati) sui risultati dalla procedura memorizzata. Se ho "Numero di pagina" e "Numero di record per pagina" come valori interi che posso passare come parametri, quale sarebbe il modo migliore per recuperare solo quella particolare sezione. Ad esempio, se passo 10 come numero di pagina e 120 come numero di pagine, dall'istruzione select mi darebbe il 1880 ° fino al 1200 °, o qualcosa del genere, i miei calcoli nella mia testa potrebbero essere sbagliati.

Lo sto facendo in .NET con C #, ho pensato che non fosse importante, se riesco a farlo bene sul lato sql, allora dovrei essere cool.

Aggiornamento: sono stato in grado di utilizzare il suggerimento di Brian e funziona alla grande. Mi piacerebbe lavorare su un po 'di ottimizzazione, ma le pagine stanno arrivando in 4 o 5 secondi invece che un minuto, e il mio controllo di paging è stato in grado di integrarsi molto bene con i miei nuovi processi memorizzati.

Risposte:


144

Qualcosa del genere dovrebbe funzionare: dal blog di Frans Bouma

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
Sì, è una colonna "incorporata" supportata da Oracle, inizia sempre da 1 e aumenta per ogni riga. Quindi in questo frammento di codice, se hai 1000 righe, viene applicato l'ordinamento e quindi a ciascuna riga viene assegnato un rownum. Le selezioni esterne utilizzano questi numeri di riga per individuare la "pagina" che stai cercando in base alle dimensioni della pagina.
Brian Schmitt

9
Questo è carino, ma orribilmente lento su grandi selezioni, basta controllare quale sarà il tempo per selezionare da 0 a 1000 e da 500.000 a 501.000 ... Stavo usando questo tipo di struttura di selezione ora sto cercando una soluzione alternativa.
Newhouse

3
@ n3whous3 potresti provare questo - inf.unideb.hu/~gabora/pagination/results.html
jasonk

7
Mi chiedevo perché due WHEREnon potessero essere combinati con AND, e poi ho
Mengdi Gao

1
L'impaginazione Oracle mi rovina la giornata.
Aetherus

134

Chiedi a Tom l'impaginazione e le funzioni analitiche molto, molto utili.

Questo è un estratto da quella pagina:

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
Questa è in realtà un'implementazione molto migliore, anche se è difficile da trovare in quel post. Quando hai molte pagine grandi, anche l'altra risposta deve superare tutte le righe delle pagine precedenti. Nelle query complicate, ciò significa che le pagine successive hanno prestazioni peggiori rispetto alle pagine precedenti.
tallseth

@tallseth Hai ragione. È difficile trovarlo su quella pagina. Viene aggiunto un estratto.
Chobicus

Questa è la risposta corretta se desideri modificare in modo dinamico il tuo ordine.
chakeda

74

Nell'interesse della completezza, per le persone che cercano una soluzione più moderna, in Oracle 12c ci sono alcune nuove funzionalità tra cui una migliore paginazione e una gestione superiore.

Paging

Il paging ha questo aspetto:

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

Top N Records

Ottenere i record migliori è simile a questo:

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

Si noti come entrambi gli esempi di query precedenti abbiano ORDER BYclausole. I nuovi comandi li rispettano e vengono eseguiti sui dati ordinati.

Non sono riuscito a trovare una buona pagina di riferimento Oracle per FETCHo OFFSETma questa pagina ha un'ottima panoramica di queste nuove funzionalità.

Prestazione

Come sottolinea @wweicker nei commenti seguenti, le prestazioni sono un problema con la nuova sintassi in 12c. Non avevo una copia di 18c per verificare se Oracle l'ha migliorato da allora.

È interessante notare che i miei risultati effettivi sono stati restituiti leggermente più velocemente la prima volta che ho eseguito le query sulla mia tabella (113 milioni + righe) per il nuovo metodo:

  • Nuovo metodo: 0,013 secondi.
  • Vecchio metodo: 0,107 secondi.

Tuttavia, come menzionato da @wweicker, il piano di spiegazione sembra molto peggiore per il nuovo metodo:

  • Costo del nuovo metodo: 300.110
  • Costo del vecchio metodo: 30

La nuova sintassi ha causato una scansione completa dell'indice sulla mia colonna, che era l'intero costo. È probabile che le cose peggiorino molto quando si limitano i dati non indicizzati.

Diamo un'occhiata quando includiamo una singola colonna non indicizzata nel dataset precedente:

  • Tempo / costo del nuovo metodo: 189,55 secondi / 998,908
  • Tempo / costo vecchio metodo: 1,973 secondi / 256

Riepilogo: utilizzare con cautela finché Oracle non migliora questa gestione. Se hai un indice con cui lavorare, forse puoi farla franca usando il nuovo metodo.

Spero di avere presto una copia di 18c con cui giocare e di poter aggiornare


Questa è un'ottima risposta per gli utenti di 12c
Lalji Gajera

1
La sintassi è più pulita, ma le prestazioni sono peggiori ( dba-presents.com/index.php/databases/oracle/… )
wweicker

Buono a sapersi, grazie @wweicker. Si spera che le prestazioni vengano presto risolte da Oracle; sebbene, conoscendo Oracle, questa potrebbe essere una speranza lontana!
JoelC

La sintassi è nuova e viene trasformata in normali chiamate ROW_NUMBER / RANK. Correlati Come si limita il numero di righe restituite da una query Oracle dopo l'ordinamento?
Lukasz Szozda

@ JoelC ci sono stati dei cambiamenti alla tua opinione?
Ryan

11

Voglio solo riassumere le risposte e i commenti. Esistono diversi modi per eseguire un'impaginazione.

Prima di Oracle 12c non c'erano le funzionalità OFFSET / FETCH, quindi dai un'occhiata al white paper come suggerito da @jasonk. È l'articolo più completo che ho trovato sui diversi metodi con spiegazione dettagliata di vantaggi e svantaggi. Ci vorrebbe una notevole quantità di tempo per copiarli e incollarli qui, quindi non lo farò.

C'è anche un buon articolo dei creatori di jooq che spiega alcuni avvertimenti comuni con Oracle e altri database impaginati. post sul blog di jooq

Buone notizie, da oracle 12c abbiamo una nuova funzionalità OFFSET / FETCH. Nuove funzionalità di OracleMagazine 12c . Fare riferimento a "Prime N query e impaginazione"

Puoi controllare la tua versione di Oracle emettendo la seguente dichiarazione

SELECT * FROM V$VERSION

7

Prova quanto segue:

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;

tramite [tecnicume]


0

Nel mio progetto ho usato Oracle 12c e java . Il codice di paging ha questo aspetto:

 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;
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.