Domanda

Quali sono alcune cose che posso fare per migliorare le prestazioni della query di una query Oracle senza creare indici?

Ecco la query che sto cercando di eseguire più velocemente:

SELECT c.ClaimNumber, a.ItemDate, c.DTN, b.FilePath
FROM items a,
itempages b,
keygroupdata c
WHERE a.ItemType IN (112,115,189,241)
AND a.ItemNum = b.ItemNum
AND b.ItemNum = c.ItemNum
ORDER BY a.DateStored DESC

Nessuna di queste colonne è indicizzata e ciascuna delle tabelle contiene milioni di record. Inutile dire che la query richiede oltre 3 minuti e mezzo. Si tratta di un database di terze parti in un ambiente di produzione e non sono autorizzato a creare alcun indice, pertanto è necessario apportare miglioramenti alle prestazioni alla query stessa.

Grazie!

È stato utile?

Soluzione

Per prima cosa riscrivo la query come standard ANSI:

SELECT c.ClaimNumber, a.ItemDate, c.DTN, b.FilePath
FROM items a
INNER JOIN itempages b ON b.ItemNum = a.ItemNum
INNER JOIN keygroupdata c ON c.ItemNum = b.ItemNum
WHERE a.ItemType IN (112,115,189,241)
ORDER BY a.DateStored DESC

Ciò semplifica la lettura e la comprensione di ciò che sta accadendo. Ti aiuta anche a non commettere errori (ad es. Cross Joining) che potrebbero causare grossi problemi. Quindi otterrei il piano Explain per vedere cosa sta facendo il DBMS con quella query. Sta cercando di utilizzare alcuni indici? Unisce correttamente le tabelle?

Quindi esaminerei le tabelle con cui sto lavorando per vedere se esistono già degli indici che potrei usare per rendere più veloce la mia query. Infine, come hanno suggerito tutti gli altri, rimuoverei la clausola Order By e lo farei solo nel codice.

Altri suggerimenti

Chiedi alla terza parte di indicizzare le sue colonne di join, come avrebbero dovuto fare in primo luogo! Senza indici, Oracle non ha altro da fare se non la forza bruta.

Puoi provare a creare una vista materializzata su una di quelle tabelle. È quindi possibile creare un indice sulla vista materializzata che contribuirà ad accelerare la query (che sarebbe quindi eseguire una query sulla vista materializzata anziché sulla tabella non elaborata).

Naturalmente, se la tabella sottostante viene aggiornata, è necessario aggiornare la vista e gli indici.

Per prima cosa, guarda il piano di esecuzione. Riflette accuratamente il numero di righe da recuperare in ogni fase dell'esecuzione della query? Quanto è selettivo il predicato "a.ItemType IN (112.115.189.241)"? Il piano di esecuzione mostra un uso dello spazio temporaneo su disco per join o ordinamenti?

In realtà, forse puoi modificare la domanda per includere il piano di esecuzione.

Assicurati anche di non avere i hash join disabilitati, cosa che a volte accade nei sistemi sintonizzati OLTP, in quanto rappresentano il modo più efficiente di integrare i dati di massa in Oracle. Dovrebbero presentarsi nel piano di esecuzione.

Puoi provare a filtrare il tipo di elemento prima di unirti ai tuoi tavoli, come mostrato qui.

Se si esegue Oracle prima della 9i, questo a volte darebbe vantaggi sorprendenti.

select 
  c.claimnumber,
  a.itemdate, 
  c.dtn,
  b.filepath
from 
  (
  select itemdate
  from items it
  where it.itemtype in(112,115,189,241)
  ) a
  itempages b,
  keygroupdata c
where a.itemnum = b.itemnum
  and b.itemnum = c.itemnum

Puoi anche provare ad aggiungere i suggerimenti / + REGOLA / o / + ORDINATO / per vedere cosa succede ... di nuovo, in particolare con le versioni precedenti, a volte dare risultati sorprendenti.

SELECT /*+RULE*/
  c.ClaimNumber, a.ItemDate, c.DTN, b.FilePath
FROM
  items a,
  itempages b,
  keygroupdata c
WHERE a.ItemType IN (112,115,189,241)
  AND a.ItemNum = b.ItemNum
  AND b.ItemNum = c.ItemNum
ORDER BY a.DateStored DESC

Se gli input della query sono costanti o prevedibili ( itemType IN (...) ), un'alternativa sarebbe quella di eseguire la query una o due volte al giorno e archiviare i risultati in un locale tabella, con indici ove appropriato.

È quindi possibile rendere la query costosa "offline" e ottenere risultati più rapidi / migliori per una query interattiva.

È una query che si esegue spesso? Sembra che sarebbe nell'interesse del proprietario del DB creare gli indici necessari per velocizzare questa query. I 3,5 minuti che trascorri eseguendo la query devono avere un impatto sul loro ambiente di produzione!

Inoltre, hanno eseguito statistiche di aggiornamento sulle tabelle? Ciò potrebbe migliorare le prestazioni, poiché l'ordine di join viene calcolato in base alle statistiche delle tabelle.

A proposito, cosa ti è permesso fare? Appena letto? Se riesci a creare tabelle temporanee e a indicizzare quelle, potrei prendere in considerazione la possibilità di creare copie temporanee della tabella, indicizzarle e quindi eseguire l'unione assistita dall'indice con le copie temporanee.

So che questo thread è molto vecchio, ma per i motori di ricerca volevo ancora offrire una soluzione alternativa che funzionasse su Oracle e che a seconda dei dati potrebbe essere molto più veloce.

with a as (
  select 
    * 
  from 
    items 
  where 
    ItemType IN (112,115,189,241)
)
SELECT 
  c.ClaimNumber
  , a.ItemDate
  , c.DTN, b.FilePath
FROM 
  a,
  itempages b,
  keygroupdata c
WHERE 
  a.ItemNum = b.ItemNum
  AND b.ItemNum = c.ItemNum
ORDER BY 
  a.DateStored DESC

Puoi anche provare il suggerimento / * + MATERIALIZE * / nella clausola WITH .

In realtà trovo che la vecchia sintassi del join di Oracle sia molto più facile da leggere di ansi sql ^^

Senza indicizzazione, la query peggiorerà solo all'aumentare della dimensione della tabella. Detto questo, prova a rimuovere la clausola order by e a fare quel tipo sul lato client.

A volte puoi vedere un vantaggio aggiungendo percorsi aggiuntivi che l'ottimizzatore può scegliere aggiungendo quelli che sembrano elementi ridondanti alla clausola where.

Ad esempio, hai A.ItemNum = B.ItemNum AND B.ItemNum = C.ItemNum. Prova ad aggiungere anche A.ItemNum = C.ItemNum. Sono abbastanza sicuro, tuttavia, che l'ottimizzatore è abbastanza intelligente da capirlo da solo - vale la pena provare comunque.

A seconda del tipo di dati della colonna ItemType potresti riscontrare un'esecuzione più rapida utilizzando quanto segue se si tratta di un varchar, Oracle eseguirà conversioni implicite.

SELECT c.ClaimNumber, a.ItemDate, c.DTN, b.FilePath
FROM items a,
itempages b,
keygroupdata c
WHERE ((a.ItemType IN ('112','115','189','241'))
AND (a.ItemNum = b.ItemNum)
AND (b.ItemNum = c.ItemNum))
ORDER BY a.DateStored DESC

Le statistiche sono raccolte su queste tabelle? In caso contrario, la raccolta di statistiche potrebbe cambiare il piano di esecuzione, anche se non sarebbe necessariamente per il meglio.

A parte questo, guarda il piano di esecuzione. Potresti vedere che sta unendo le tabelle in un ordine non ottimale (ad es. Potrebbe unire bec prima di unirsi a un che ha la condizione di filtro).

È possibile utilizzare i suggerimenti per provare a influenzare i percorsi di accesso, l'ordine dei join o il metodo join.

Aggiornamento : rispondere al commento mi ha portato a questo presentazione, che potrebbe essere utile o almeno interessante.

Se dici che non ci sono indici, significa che non ci sono chiavi primarie o esterne definite? Ovviamente l'analisi delle tabelle e la raccolta delle statistiche sono importanti ma se non esistono metadati come la definizione di come le tabelle dovrebbero essere unite, Oracle potrebbe scegliere un percorso di esecuzione scadente.

In tal caso, l'utilizzo di un suggerimento come / * + ORDERED * / potrebbe essere l'unica opzione per fare in modo che l'ottimizzatore scelga in modo affidabile un buon percorso di esecuzione. Potrebbe anche valere la pena aggiungere chiavi esterne e chiavi primarie, ma definirle come DISABILITATE e CONVALIDA.

Immagino che l'utilità di questo commento dipenda dalla misura in cui l'avversione agli indici arriva così YMMV.

Innanzitutto crea una vista su questa query e quindi genera una tabella da questa vista. Crea anche un indice alla data, crea un lavoro e pianificalo a mezzanotte quando il sistema è inattivo.

Bene, dato che non puoi creare indici, mi assicurerei che le statistiche siano tutte aggiornate, riscriverei la query in questo modo:

con un as (seleziona / * + MATERIALIZE * / ItemType, ItemNum, DateStored, ItemDate dagli articoli in cui ItemType in (112.115.189.241)) SELEZIONA c.ClaimNumber, a.ItemDate, c.DTN, b.FilePath Da un, itempages b, keygroupdata c DOVE a.ItemNum = b.ItemNum AND b.ItemNum = c.ItemNum ORDINA DA a.DateStored DESC

Rimuovi ORDER BY

esegui l'ordinamento, dopo aver riportato le righe nella tua applicazione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top