Come posso velocizzare row_number in Oracle?
-
05-07-2019 - |
Domanda
Ho una query SQL che assomiglia a questa:
SELECT * FROM(
SELECT
...,
row_number() OVER(ORDER BY ID) rn
FROM
...
) WHERE rn between :start and :end
In sostanza, è la parte ORDER BY che rallenta le cose. Se dovessi rimuoverlo, il costo EXPLAIN diminuisce 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 'più di tempo con lo strumento EXPLAIN?
Soluzione
ROW_NUMBER
è abbastanza inefficiente in Oracle
.
Vedi l'articolo nel mio blog per i dettagli sulle prestazioni:
Per la tua query specifica, ti consiglio di sostituirlo con ROWNUM
e assicurarti che venga utilizzato l'indice:
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
Assicurati anche che la colonna
non sia nullable, oppure aggiungi la condizione WHERE IS NOT NULL
.
Altrimenti l'indice non può essere utilizzato per recuperare tutti i valori.
Nota che non puoi usare ROWNUM TRA: inizio e: fine
senza una sottoquery.
ROWNUM
è sempre assegnato per ultimo e controllato per ultimo, in questo modo ROWNUM
è sempre in ordine senza lacune.
Se si utilizza ROWNUM TRA 10 e 20
, la prima riga che soddisfa tutte le altre condizioni diventerà un candidato per la restituzione, assegnata temporaneamente con ROWNUM = 1
e fallire il test di ROWNUM TRA 10 E 20
.
Quindi la riga successiva sarà candidata, assegnata con ROWNUM = 1
e fallita, ecc., quindi, alla fine, nessuna riga verrà restituita.
Questo dovrebbe essere aggirato inserendo ROWNUM
nella sottoquery.
Altri suggerimenti
Mi sembra una query di impaginazione.
Da questo articolo di ASKTOM (circa il 90% in fondo alla pagina):
Anche le tue domande non sono quasi uguali, quindi non sono sicuro di quale sia il vantaggio di confrontare i costi dell'uno con l'altro.
La colonna ORDER BY è indicizzata? Altrimenti è un buon punto di partenza.
Parte del problema è quanto è grande l'intervallo 'inizio' a 'fine' e dove 'vivono'. Supponi di avere un milione di righe nella tabella e desideri che le righe siano comprese tra 567.890 e 567.900, quindi dovrai convivere con il fatto che dovrà passare attraverso l'intera tabella, ordinando praticamente tutto per id, e capire quali righe rientrano in tale intervallo.
In breve, è un sacco di lavoro, motivo per cui l'ottimizzatore gli dà un costo elevato.
Inoltre, non è qualcosa che un indice può aiutare con molto. Un indice darebbe l'ordine, ma nella migliore delle ipotesi ti darà un punto da cui iniziare e poi continuerai a leggere fino a raggiungere la 567.900 voci.
Se stai mostrando all'utente 10 elementi alla volta, potrebbe valere la pena afferrare i primi 100 dal DB, quindi fare in modo che l'app divida i 100 in dieci blocchi.
Trascorri più tempo con lo strumento EXPLAIN PLAN. Se vedi una TABELLA SCANSIONE devi modificare la tua query.
La tua domanda non ha molto senso per me. Interrogare su un ROWID sembra chiedere problemi. Non ci sono informazioni relazionali in quella query. È la vera query con cui stai riscontrando problemi o un esempio che hai inventato per illustrare il tuo problema?