[EDIT ULTIME]
mio RISPOSTA ORIGINALE quanto riguarda la creazione dell'indice appropriato (nome, id) per sostituire l'indice (nome) è sotto. (Questa non era una risposta alla domanda originale, che non consentiva modifiche al database.)
Ecco le dichiarazioni che ho non ancora testato. Probabilmente c'è qualche ovvia ragione per cui questi non funzioneranno. Non avevo mai realmente suggerisco dichiarazioni di scrittura come questo (con il rischio di essere inculcato a fondo per tale suggerimento ridicolo.)
Se queste query anche restituire set di risultati, il set ressult sarà simile solo il risultato impostato dal OP query, quasi per errore, sfruttando una garanzia originale sui dati che Don ci ha fornito. Questa dichiarazione NON è equivalente all'SQL originale, queste istruzioni sono progettate per il caso speciale come descritto da Don.
select m1.id
, m2.name
from (select min(t1.rowid) as min_rowid
, t1.id
from table1 t1
where t1.id is not null
group by t1.id
) m1
, (select min(t2.rowid) as min_rowid
, t2.name from table1 t2
where t2.name is not null
group by t2.name
) m2
where m1.min_rowid = m2.min_rowid
order
by m1.id
Diamo disfare che:
- m1 è una vista in linea che ci ottiene un elenco di valori id distinti.
- m2 è una vista in linea che ottiene un elenco di valori nome distinti.
- materializzano i punti di vista m1 e m2
- partita l'identificativo da m1 e m2 da abbinare
id
con name
Qualcun altro ha suggerito l'idea di un merge indice. In precedenza avevo respinto quell'idea, un piano di ottimizzazione per abbinare 10 di milioni di rowid senza eliminarne nessuno.
Con sufficientemente bassa cardinalità per id e il nome, e con il piano di ottimizzazione destra:
select m1.id
, (select m2.name
from table1 m2
where m2.id = m1.id
and rownum = 1
) as name
from (select t1.id
from table1 t1
where t1.id is not null
group by t1.id
) m1
order
by m1.id
Diamo disfare che
- m1 è una vista in linea che ci ottiene un elenco di distinta valori di identificazione.
- materializzare la vista m1
- per ogni riga m1, interrogare table1 per ottenere il valore del nome da una singola fila (stopkey)
NOTA IMPORTANTE
Queste dichiarazioni sono FONDAMENTALMENTE diversi dalla query OP. Sono progettati per restituire un set di risultati DIFFERENT rispetto alla query OP. Il si verifica per restituire il set di risultati desiderato a causa di una garanzia non convenzionale sui dati. Don ci ha detto che uno name
è determinato da id
. (È vero il contrario? È determinato da name
? Abbiamo una GARANZIA STATA, non necessariamente applicata dal database, ma una garanzia che possiamo sfruttare?) Per qualsiasi valore ID
, ogni riga con quel valore ID
avrà lo stesso valore NAME
. (E ci sono anche garantiti il contrario è vero, che per ogni valore NAME
, ogni riga con quel valore NAME
avrà lo stesso valore di ID
?)
Se è così, forse possiamo fare uso di tali informazioni. Se ID
e NAME
vengono visualizzati in coppie distinte, è sufficiente trovare una riga specifica. La "coppia" avrà un ROWID di corrispondenza, che per convenienza risulta essere disponibile da ciascuno degli indici esistenti. Cosa succede se otteniamo il ROWID minimo per ogni ID
e ottieni il ROWID minimo per ogni NAME
. Non è quindi possibile abbinare lo ID
allo NAME
in base al ROWID che contiene la coppia? Penso che potrebbe funzionare, data una cardinalità abbastanza bassa. (Cioè, se abbiamo a che fare solo con centinaia di ROWIDs piuttosto che 10s di milioni.)
[/ ULTIME EDIT]
[EDIT]
La domanda è ora aggiornato con le informazioni relative alla tabella, mostra che la colonna ID
e la colonna NAME
consentono entrambi i valori NULL. Se Don può vivere senza alcun valore NULL restituito nel set di risultati, quindi l'aggiunta del predicato IS NOT NULL su entrambe le colonne potrebbe consentire l'utilizzo di un indice. (NOTA: in un indice Oracle (B-Tree), i valori NULL NON appaiono nell'indice.)
[/ EDIT]
ORIGINALE RISPOSTA:
creare un indice appropriato
create index table1_ix3 on table_1 (name,id) ... ;
Okay, questo è non la risposta alla domanda che hai posto, ma è la risposta giusta per risolvere il problema delle prestazioni. (Non hai specificato modifiche al database, ma in questo caso la modifica del database è la risposta giusta.)
Nota che se hai un indice definito su (name,id)
, allora (molto probabilmente) non è necessario un indice su (name)
, l'ottimizzatore considererà la colonna principale name
nell'altro indice.
(UPDATE: come qualcuno più astuto di quanto ho fatto notare, non avevo nemmeno preso in considerazione la possibilità che gli indici esistenti erano indici bitmap e non indici B-tree ...)
Re- valutare la necessità del set di risultati ... è necessario restituire id
oppure restituire name
essere sufficiente.
select distinct name from table1 order by name;
Per un particolare nome, si potrebbe presentare una seconda interrogazione per ottenere l'associato id
, se e quando lo desideravi ...
select id from table1 where name = :b1 and rownum = 1;
Se si ha realmente bisogno il set di risultati specificato, puoi provare alcune alternative per vedere se le prestazioni sono migliori. Non nutro molte speranze per uno di questi:
select /*+ FIRST_ROWS */ DISTINCT id, name from table1 order by id;
o
select /*+ FIRST_ROWS */ id, name from table1 group by id, name order by name;
o
select /*+ INDEX(table1) */ id, min(name) from table1 group by id order by id;
UPDATE: come altri hanno acutamente sottolineato, con questo approccio noi' per testare e confrontare le prestazioni di query alternative, che è una sorta di approccio "hit & miss". (Non sono d'accordo sul fatto che sia casuale, ma concordo sul fatto che sia casuale.)
AGGIORNAMENTO: tom suggerisce il suggerimento ALL_ROWS. Non l'avevo considerato, perché ero davvero concentrato sull'ottenere un piano di query usando un INDICE. Sospetto che la query OP stia eseguendo una scansione completa della tabella e probabilmente non è la scansione che sta impiegando del tempo, è l'operazione di ordinamento univoco (< 10g) o un'operazione di hash (10gR2 +) che richiede tempo. (Assente statistiche temporizzate e traccia dell'evento 10046, sto solo indovinando qui.) Ma poi di nuovo, forse è la scansione, chissà, l'alta marea sul tavolo potrebbe essere uscita in una vasta distesa di blocchi vuoti.
Inutile dire che le statistiche sulla tabella devono essere aggiornate e dovremmo utilizzare SQL * Plus AUTOTRACE o almeno ESPLINARE PIANO per esaminare i piani di query.
Ma nessuna delle query alternative suggerite risolve il problema delle prestazioni.
È possibile che gli hint influenzino l'ottimizzatore per selezionare un piano diverso, sostanzialmente soddisfacendo l'ORDER BY da un indice, ma non mi aspetto molte speranze. (Non credo che l'hint FIRST_ROWS funzioni con GROUP BY, potrebbe essere il suggerimento INDEX.) Posso vedere il potenziale per un simile approccio in uno scenario in cui ci sono tracce di blocchi di dati vuoti e scarsamente popolati e che accedono ai dati blocca attraverso un indice, potrebbe effettivamente essere significativamente meno blocchi di dati tirati in memoria ... ma quello scenario sarebbe l'eccezione piuttosto che la norma.
UPDATE: Come Rob van Wijk sottolinea, facendo uso della funzione di traccia Oracle è l'approccio più efficace per identificare e risolvere problemi di prestazioni.
Senza l'output di un output di EXPLAIN PLAN o SQL * Plus AUTOTRACE, sto solo indovinando qui.
Sospetto che il problema di prestazioni in questo momento sia che i blocchi di dati della tabella devono essere referenziati per ottenere il set di risultati specificato.
Non c'è niente da fare intorno ad esso, la query non può essere soddisfatto da solo un indice, dal momento che non v'è un indice che contiene sia le NAME
e ID
colonne, sia con la colonna ID
o NAME
come la colonna principale. Le altre due query OP "veloci" possono essere soddisfatte dall'indice senza bisogno di fare riferimento alla riga (blocchi di dati).
Anche se il piano di ottimizzazione per la query utilizzava uno degli indici, deve ancora recuperare la riga associata dal blocco di dati, per ottenere il valore per l'altra colonna. E senza predicato (nessuna clausola WHERE), l'ottimizzatore sta probabilmente optando per una scansione completa della tabella e probabilmente eseguendo un'operazione di ordinamento (< 10g). (Anche in questo caso, un PIANO DI SPIEGAZIONE mostrerebbe il piano di ottimizzazione, come sarebbe AUTOTRACE.)
Sto anche assumendo qui (grande ipotesi) che entrambe le colonne siano definite come NOT NULL.
Si potrebbe anche considerare di definire la tabella come una tabella organizzata indice (IOT), soprattutto se queste sono le uniche due colonne nella tabella. (Un IOT non è una panacea, si tratta con il proprio insieme di problemi di prestazioni.)
Si può provare a riscrivere la query (a meno che questo è un cambiamento database che è anche verboten) Nei nostri ambienti di database , consideriamo una query come parte del database come le tabelle e gli indici.)
Anche in questo caso, senza un predicato, l'ottimizzatore probabilmente non utilizzerà un indice. C'è una possibilità che si potrebbe ottenere il piano di query per utilizzare uno degli indici esistenti per ottenere le prime righe restituite in fretta, con l'aggiunta di un pizzico, provare una combinazione di:
select /*+ INDEX(table1) */ ...
select /*+ FIRST_ROWS */ ...
select /*+ ALL_ROWS */ ...
distinct id, name from table1;
distinct id, name from table1 order by id;
distinct id, name from table1 order by name;
id, name from table1 group by id, name order by id;
id, min(name) from table1 group by id order by id;
min(id), name from table1 group by name order by name;
Con un suggerimento, si può essere in grado di influenza l'ottimizzatore per l'uso di un indice e questo può evitare l'operazione di ordinamento, ma nel complesso richiede più tempo per restituire l'intero set di risultati.
(AGGIORNAMENTO: qualcun altro ha sottolineato che l'ottimizzatore potrebbe scegliere di unire due indici in base a ROWID.Questa è una possibilità, ma senza un predicato per eliminare alcune righe, è probabile che sarà un approccio molto più costoso (corrispondente a 10 s di milioni ROWID) da due indici, specialmente quando nessuna delle righe verrà esclusa sulla base del match.)
Ma tutto ciò che teorizza non equivale a squat senza alcune statistiche sulle prestazioni.
Assente alterare qualsiasi altra cosa nel database, l'unica altra speranza (mi viene in mente) di voi accelerare la query è quello di assicurarsi l'operazione di ordinamento è sintonizzato in modo che il (richiesto) operazione di ordinamento può essere eseguito in memoria, piuttosto che su disco. Ma non è davvero la risposta giusta. L'ottimizzatore potrebbe non eseguire alcuna operazione di ordinamento, potrebbe invece eseguire un'operazione di hash (10gR2 +), nel qual caso, dovrebbe essere regolato. L'operazione di ordinamento è solo una supposizione da parte mia, sulla base dell'esperienza passata con Oracle 7.3, 8, 8i, 9i.)
Un DBA grave sta per avere più problema con voi futzing con la SORT_AREA_SIZE
e/o HASH_AREA_SIZE
parametri per la tua sessione (s) di quello che farà nella creazione degli indici corretti. (E quei parametri di sessione sono "old school" per versioni precedenti alla 10g gestione automatica della memoria magica.)
Mostra al tuo DBA le specifiche per il set di risultati, lascia che il DBA si sintonizzi.
È possibile aggiungere una clausola WHERE per limitare rapidamente il set di risultati? Non sono sicuro che lo stiate già facendo anche se gli esempi mostrati non hanno una logica di predicato. :) – tom
Quante righe vengono restituite da DISTINCT ID e DISTINCT NAME? – Quassnoi
33 righe per ciascuno. –