2011-09-24 12 views
7

La mia domanda riguarda Oracle 11g e l'uso di indici nelle query SQL.Oracle 11g: indice non utilizzato in "select distinct" -query

nel mio database, c'è un tavolo che è strutturato come segue:

Table tab (
    rowid NUMBER(11), 
    unique_id_string VARCHAR2(2000), 
    year NUMBER(4), 
    dynamic_col_1 NUMBER(11), 
    dynamic_col_1_text NVARCHAR2(2000) 
) TABLESPACE tabspace_data; 

ho creato due indici:

CREATE INDEX Index_dyn_col1 ON tab (dynamic_col_1, dynamic_col_1_text) TABLESPACE tabspace_index; 
CREATE INDEX Index_unique_id_year ON tab (unique_id_string, year) TABLESPACE tabspace_index; 

La tabella contiene circa 1 a 2 milioni di dischi. Estraggo i dati da esso eseguendo il seguente comando SQL:

SELECT distinct 
"sub_select"."dynamic_col_1" "AS_dynamic_col_1","sub_select"."dynamic_col_1_text" "AS_dynamic_col_1_text" 
FROM 
(
    SELECT "tab".* FROM "tab" 
    where "tab".year = 2011 
) "sub_select" 

Sfortunatamente, la query richiede circa 1 ora per eseguire, anche se ho creato gli indici entrambi sopra descritti. Il piano spiega che Oracle utilizza un "Accesso completo alla tabella", ovvero una scansione completa della tabella. Perché l'indice non viene utilizzato?

Come esperimento, ho testato il seguente comando SQL:

SELECT DISTINCT 
"dynamic_col_1" "AS_dynamic_col_1", "dynamic_col_1_text" "AS_dynamic_col_1_text" 
FROM "tab" 

Anche in questo caso, l'indice non viene utilizzato e viene eseguita una scansione completa della tabella.

Nel mio vero database, la tabella contiene più colonne indicizzate come "dynamic_col_1" e "dynamic_col_1_text". L'intero file indice ha una dimensione di circa 50 GB.

Un paio di informazioni:

  • il database è Oracle 11g installato sul mio computer locale.
  • Uso Windows 7 Enterprise 64 bit.
  • L'intero indice è suddiviso su 3 file dbf con circa 50 GB di dimensione.

Sarei davvero felice se qualcuno potesse dirmi come rendere Oracle l'indice nella prima query. Poiché la prima query viene utilizzata da un altro programma per estrarre i dati dal database, difficilmente può essere modificata. Quindi sarebbe bene modificare il tavolo invece.

Grazie in anticipo.

[01.10.2011: UPDATE]

Credo di aver trovato la soluzione per il problema. Entrambe le colonne dynamic_col_1 e dynamic_col_1_text sono annullabili. Dopo aver modificato la tabella per vietare i valori "NULL" in entrambe le colonne e aggiungere un nuovo indice esclusivamente per la colonna year, Oracle esegue una scansione dell'indice rapido. Il vantaggio è che la query richiede ora circa 5 secondi per l'esecuzione e non 1 ora come prima.

+0

Il campo 'rowid' è la chiave primaria? –

+0

Si prega di non chiamare una colonna in un rowid tabella, stai chiedendo un mondo di dolore se hai mai avuto bisogno di utilizzare il vero [rowid pseudo-colonna] (http://download.oracle.com/docs/cd /B19306_01/server.102/b14200/pseudocolumns008.htm) – Ben

+0

@BogdanSahlean Sì, rowid è la chiave primaria –

risposta

1

Non so se è rilevante, ma ho provato la seguente query:

SELECT DISTINCT 
"dynamic_col_1" "AS_dynamic_col_1", "dynamic_col_1_text" "AS_dynamic_col_1_text" 
FROM "tab" 
WHERE "dynamic_col_1" = 123 AND "dynamic_col_1_text" = 'abc' 

Il piano per quello spettacolo query che Oracle utilizza un indice di scansione in questo scenario spiegare.

Le colonne dynamic_col_1 e dynamic_col_1_text sono annullabili. Questo ha un effetto sull'uso dell'indice?

01.10.2011: UPDATE]

Credo di aver trovato la soluzione per il problema. Entrambe le colonne dynamic_col_1 e dynamic_col_1_text sono annullabili. Dopo aver modificato la tabella per proibire i valori "NULL" in entrambe le colonne e aggiungere un nuovo indice esclusivamente per l'anno della colonna, Oracle esegue una scansione dell'indice rapido. Il vantaggio è che la query richiede ora circa 5 secondi per l'esecuzione e non 1 ora come prima.

2

Non ho un'istanza Oracle a portata di mano, quindi questo è un po 'approssimativo, ma la mia inclinazione è dire che è perché l'indice composto è nell'ordine sbagliato. Se aveste year come prima colonna dell'indice, potrebbe usarlo.

4

L'indice dovrebbe essere:

CREATE INDEX Index_year 
ON tab (year) 
TABLESPACE tabspace_index; 

Inoltre, la query potrebbe essere solo:

SELECT DISTINCT 
     dynamic_col_1 "AS_dynamic_col_1", 
     dynamic_col_1_text "AS_dynamic_col_1_text" 
    FROM tab 
WHERE year = 2011; 

Se l'indice è stato creato esclusivamente per questa query, però, è possibile creare esso compreso i due inverosimile anche le colonne, quindi l'ottimizzatore non dovrebbe andare alla tabella per i dati della query, potrebbe recuperarlo direttamente dall'indice rendendo la tua query ancora più efficiente.

Speranza che aiuta ...

+1

Supponendo che @jonearles non sia corretto e che l'indice sia d'aiuto e che usi la tua query (che direi). L'indice migliore sarebbe 'year, dynamic_col_1, dynamic_col_1_text' dato che non avrebbe bisogno di accedere nuovamente alla tabella da rowid (il rowid reale) dopo. Poteva semplicemente prendere tutti i dati dall'indice. – Ben

+0

@Ollie ci ho già pensato. Ma se rimuovo completamente la colonna 'year' dalla query ed eseguo' SELECT DISTINCT "dynamic_col_1" "AS_dynamic_col_1", "dynamic_col_1_text" "AS_dynamic_col_1_text" FROM "tab" ', anche l'indice non viene usato. –

+0

@ Ben L'esempio sopra riportato dovrebbe solo illustrare il problema. Nel database reale ci sono oltre 200 hundret di quelle colonne 'dynamic_col_1' e' dynamic_col_1_text' che devono essere indicizzate. Suppongo che usare un indice BIG su 500 colonne non sia una buona idea ?! –

5

Sei sicuro che un accesso di indice sarebbe più veloce di un tavolo scansione completa? Come stima molto approssimativa, le scansioni complete della tabella sono 20 volte più veloci rispetto alla lettura di un indice. Se lo tab ha più del 5% dei dati nel 2011, non sorprende che Oracle utilizzi una scansione completa della tabella. E come menzionato @Dan e @Ollie, con year come seconda colonna questo renderà l'indice ancora più lento.

Se l'indice è davvero più veloce, il problema è probabilmente una cattiva statistica. Ci sono centinaia di modi in cui le statistiche potrebbero essere cattive. Molto brevemente, ecco cosa guarderei prima:

  1. Esegui un piano di spiegazioni con e senza e un suggerimento indice. Le cardinalità sono diminuite di 10 volte o più? I tempi sono diminuiti di 10 volte o più?
  2. Se la cardinalità è disattivata, assicurati che ci siano statistiche aggiornate sulla tabella e sull'indice e stai utilizzando un ESTIMATE_PERCENT ragionevole (DBMS_STATS.AUTO_SAMPLE_SIZE è quasi sempre il migliore per 11g).
  3. Se l'ora è disattivata, controllare le statistiche del carico di lavoro.
  4. Stai usando il parallelismo? Oracle assume sempre un miglioramento quasi lineare per il parallelismo, ma su un desktop con un disco fisso probabilmente non vedrai alcun miglioramento.

Inoltre, questo non è veramente rilevante per il tuo problema, ma potresti voler evitare di usare gli identificatori citati. Una volta che li usi devi usarli ovunque, e in genere rende dolorose le tue tabelle e le tue domande.

+0

+1, le statistiche non aggiornate sono la causa di molti problemi di prestazioni. Buona richiesta sulla percentuale di record a cui l'OP accederà. Un FTS potrebbe effettivamente essere il metodo di accesso più performante se sta recuperando una buona percentuale dei record della tabella. – Ollie

+0

@Ollie Bene, correggimi se ho torto, ma ho pensato che usando un indice su entrambe le colonne "dynamic_col_1 e dynamic_col_1_text avrebbero accelerato la query" select distinct dynamic_col_1, dynamic_col_1_text ... ". Difficilmente, non riesco a cambiare il query sql perché è generata da un altro strumento per estrarre i dati dalla tabella, quindi quegli identificatori citati sono inevitabili. –

+0

@ oracle_user54 il punto in cui Ollie e jonearles stanno arrivando è che gli indici non sono sempre più veloci di un FTS.Se la percentuale di righe che stai per tirare è sufficientemente grande, FTS è più veloce perché (approssimativamente) il sovraccarico di andare avanti e indietro verso l'indice è maggiore del guadagno di velocità di non eseguire la scansione del resto delle righe. – Dan

0

Prova questo:

1) Creare un indice sul campo l'anno (vedi Ollie risposta).

2) e quindi utilizzare questa query:

SELECT DISTINCT 
dynamic_col_1 
,dynamic_col_1_text 
FROM tab 
WHERE ID (SELECT ID FROM tab WHERE year=2011) 

o

SELECT DISTINCT 
dynamic_col_1 
,dynamic_col_1_text 
FROM tab 
WHERE ID (SELECT ID FROM tab WHERE year=2011) 
GROUP BY dynamic_col_1, dynamic_col_1_text 

Forse vi aiuterà.

1

vostra seconda query di prova:

SELECT DISTINCT 
"dynamic_col_1" "AS_dynamic_col_1", "dynamic_col_1_text" "AS_dynamic_col_1_text" 
FROM "tab" 

non avrebbero utilizzato l'indice perché non hai clausola WHERE, in modo che stai chiedendo Oracle di leggere ogni riga della tabella. In tale situazione, la scansione completa della tabella è il metodo di accesso più rapido.

Inoltre, come menzionato da altri utenti, il tuo indice su ANNO ce l'ha nella seconda colonna. Oracle può utilizzare questo indice eseguendo una scansione skip, ma c'è un calo di prestazioni per farlo e, a seconda delle dimensioni della tabella, Oracle potrebbe decidere di utilizzare nuovamente l'FTS.

Problemi correlati