2009-05-05 23 views
11

Ho un query SQL che sembra qualcosa di simile:Come posso aumentare il numero di riga in Oracle?

SELECT * FROM(
    SELECT 
     ..., 
     row_number() OVER(ORDER BY ID) rn 
    FROM 
     ... 
) WHERE rn between :start and :end 

Essenzialmente, è ORDER BY parte che sta rallentando il tutto. Se dovessi rimuoverlo, il costo di SPIEGARE scende di un ordine di grandezza (oltre 1000x). Ho provato questo:

SELECT 
    ... 
FROM 
    ... 
WHERE 
    rownum between :start and :end 

Ma questo non dà risultati corretti. C'è un modo semplice per accelerare questo? O dovrò passare un po 'di tempo con lo strumento EXPLAIN?

risposta

12

ROW_NUMBER è abbastanza inefficiente in Oracle.

vedere l'articolo nel mio blog per i dettagli delle prestazioni:

Per la vostra domanda specifica, vi consiglio di sostituire con ROWNUM e assicurarsi che l'indice è utilizzato :

SELECT * 
FROM (
     SELECT /*+ INDEX_ASC(t index_on_column) NOPARALLEL_INDEX(t index_on_column) */ 
       t.*, ROWNUM AS rn 
     FROM table t 
     ORDER BY 
       column 
     ) 
WHERE rn >= :start 
     AND rownum <= :end - :start + 1 

Questa query utilizzerà COUNT STOPKEY

Inoltre, assicurati di non avere il valore n. column o aggiungere la condizione WHERE column IS NOT NULL.

In caso contrario, non è possibile utilizzare l'indice per recuperare tutti i valori.

Nota che non è possibile utilizzare ROWNUM BETWEEN :start and :end senza subquery.

ROWNUM viene sempre assegnato per ultimo e controllato per ultimo, in questo modo ROWNUM vengono sempre ordinati senza interruzioni.

Se si utilizza ROWNUM BETWEEN 10 and 20, la prima riga che soddisfa tutte le altre condizioni diventerà un candidato per la restituzione, temporaneamente assegnata con ROWNUM = 1 e non riuscirà il test di ROWNUM BETWEEN 10 AND 20.

Quindi la riga successiva sarà un candidato, assegnato con ROWNUM = 1 e non riuscito, ecc., Quindi, infine, non verrà restituita alcuna riga.

Questo dovrebbe essere risolto inserendo ROWNUM nella sottoquery.

+0

Funziona come un Tuttavia, i suggerimenti per l'ottimizzazione non sembrano fare una differenza apprezzabile: –

+3

Ciò significa che 'CBO' era abbastanza intelligente da raccogliere gli indici.In realtà era ROWNUM invece ROW_NUMBER che contava qui. – Quassnoi

+0

Ma vorrei ancora lasciare i suggerimenti o creato una struttura, nel caso in cui il CBO cambierà idea :) – Quassnoi

1

La colonna ORDER BY è indicizzata? Se non è un buon punto di partenza.

+0

In realtà, non lo era. Ma cambiarlo in una riga indicizzata da IS non aiuta. Grazie per aver fatto l'ovvio suggerimento però. :-) –

+1

Un indice aiuterebbe solo a migliorare ORDER BY se il percorso di accesso potrebbe utilizzare quell'indice (cioè, si stava cercando un intervallo di ID). –

0

Trascorri più tempo con lo strumento ESPLORA PIANO. Se vedi una TABELLA SCANSIONE devi cambiare la tua richiesta.

La tua richiesta ha poco senso per me. Interrogare su un ROWID sembra come chiedere dei guai. Non ci sono informazioni relazionali in quella query. È la vera query con cui hai problemi o un esempio che hai inventato per illustrare il tuo problema?

+0

È l'impaginazione. E questo è essenzialmente ciò che la query fa almeno con il paging. Ho appena preso il resto della query (principalmente perché non è banale). Tutte le ellissi sono dove ho tagliato fuori cose per brevità. –

4

Sembra una query di impaginazione per me.

Da questo articolo ASKTOM (circa il 90% in basso nella pagina):

You need to order by something unique for these pagination queries, so that ROW_NUMBER is assigned deterministically to the rows each and every time.

anche le vostre domande sono non vicino a dove lo stesso quindi non sono sicuro di quello che il beneficio di confrontare i costi di uno all'altro è.

+1

In realtà, quell'articolo mi ha aiutato a scrivere la query. Tuttavia, non ho notato la parte relativa all'ordinamento per ID unici. Inoltre c'è un suggerimento di Query Optimizer che ho perso. Ci proverò domani al lavoro! –

+0

;) pensato che sembrava familiare. first_rows può essere sorprendente con le query di impaginazione. – David

+0

Questo e il consiglio di Quassnoi hanno portato la mia richiesta a un tempo quasi costante! Vorrei poter selezionare due risposte. :-( –

1

Parte del problema è quanto grande è il 'inizio' a 'fine' intervallo e dove essi 'live'. Supponiamo che tu abbia un milione di righe nella tabella, e vuoi le righe da 567,890 a 567,900, quindi dovrai convivere con il fatto che sarà necessario passare attraverso l'intera tabella, in modo tale da ordinare praticamente tutto ciò con ID e calcola quali file rientrano in tale intervallo.

In breve, è molto lavoro, motivo per cui l'ottimizzatore offre un costo elevato.

Non è nemmeno qualcosa che un indice può aiutare con molto. Un indice darebbe l'ordine, ma nella migliore delle ipotesi ciò ti darà un posto dove iniziare e poi continuerai a leggere finché non arriverai alla 567.900.

Se si visualizzano 10 elementi dell'utente alla volta, potrebbe valere la pena afferrare i primi 100 dal DB, quindi fare in modo che l'app interrompa 100 in dieci blocchi.

+0

Sembra appropriato: sto realizzando circa 15.000 record su ~ 2 milioni di record, siamo limitati dalla quantità di tempo che una query può richiedere e l'estrazione di tutti i record 15k contemporaneamente causava un timeout. Ho pensato che sfogliare i risultati avrebbe impedito tutto questo, suppongo che questo significhi che dovrò passare attraverso l'incubo burocratico di richiedere un timeout più lungo –

+0

Spero che non invii 15.000 righe all'utente! –

Problemi correlati