2009-04-06 14 views
15

Abbiamo Oracle 10g e dobbiamo interrogare 1 tabella (senza join) e filtrare le righe in cui 1 delle colonne è nullo. Quando lo facciamo - WHERE OurColumn NON È NULL - otteniamo una scansione completa della tabella su una tabella molto grande - BAD BAD BAD. La colonna ha un indice su di esso ma viene ignorata in questa istanza. Ci sono soluzioni a questo?Oracle 10g - ottimizzazione DOVE NON È NULL

Grazie

risposta

20

L'ottimizzatore ritiene che la scansione completa della tabella sarà migliore.

Se ci sono solo poche righe NULL, l'ottimizzatore ha ragione.

Se siete assolutamente sicuri che l'accesso indice sarà più veloce (cioè, avete più di 75% righe con col1 IS NULL), quindi suggerire la query:

SELECT /*+ INDEX (t index_name_on_col1) */ 
     * 
FROM mytable t 
WHERE col1 IS NOT NULL 

Perché 75%?

Poiché l'utilizzo di per recuperare valori non coperti dall'indice implica un join nascosto su ROWID, che costa circa 4 volte tanto quanto la scansione della tabella.

Se l'intervallo di indice include più di 25% di righe, la scansione della tabella è in genere più veloce.

Come menzionato da Tony Andrews, il fattore di raggruppamento è il metodo più accurato per misurare questo valore, ma 25% è ancora una buona regola empirica.

+1

Quassnoi, dove stai ottenendo quel 75%? Se ci sono un milione di righe e solo una è nullo, perché usare un indice su quelle colonne è più lento di una scansione della tabella? – tpdi

+1

Poiché l'indice impedisce l'unione nascosta su ROWID, che costa circa 4 volte tanto quanto la scansione della tabella. La selettività dell'indice è inferiore al 25%, la scansione della tabella è solitamente più veloce. – Quassnoi

+2

In una scansione completa della tabella, si esegue semplicemente un'iterazione su tutte le righe della tabella; se fai una scansione dell'indice, devi prima leggere l'indice e poi leggere la tabella. Da un certo punto il costo della lettura di un indice è più alto della semplice lettura dell'intera tabella. – andri

2

Se stai facendo una select *, allora avrebbe senso fare una scansione di tabella, piuttosto che utilizzando l'indice. Se sai quali colonne ti interessano, potresti creare un indice coperto con quelle colonne più quella che stai applicando la condizione IS NOT NULL.

0

Creare un indice su quella colonna.

Per assicurarsi che l'indice sia utilizzato, dovrebbe essere sull'indice e su altre colonne nel dove.

ocdecio rispose:

Se stai facendo una select *, allora avrebbe senso fare una tabella di scansione piuttosto che utilizzare l'indice.

Questo non è assolutamente vero; verrà utilizzato un indice se è presente un indice che si adatta alla clausola where e Query Optimizer decide di utilizzare tale indice più rapidamente di una scansione della tabella. Se non esiste alcun indice o nessun indice adatto, solo allora è necessario eseguire una scansione della tabella.

+0

Grandi commenti su Select * Giusto per chiarire però - non usiamo mai SELECT * per altri motivi - includiamo sempre la nostra lista di colonne nelle clausole SELECT. –

15

L'ottimizzatore prende la decisione in base al costo relativo della scansione completa della tabella e all'utilizzo dell'indice. Ciò dipende principalmente dal numero di blocchi da leggere per soddisfare la query. La regola del 25%/75% menzionata in un'altra risposta è semplicistica: in alcuni casi una scansione completa della tabella avrà senso anche per ottenere l'1% delle righe, ad esempio se queste righe vengono distribuite su molti blocchi.

Ad esempio, considerare questa tabella:

SQL> create table t1 as select object_id, object_name from all_objects; 

Table created. 
SQL> alter table t1 modify object_id null; 

Table altered. 

SQL> update t1 set object_id = null 
    2 where mod(object_id,100) != 0 
    3/

84558 rows updated. 

SQL> analyze table t1 compute statistics; 

Table analyzed. 

SQL> select count(*) from t1 where object_id is not null; 

    COUNT(*) 
---------- 
     861  

Come si può vedere, solo approssimativamente 1% delle righe T1 hanno un object_id non nullo.Ma a causa del modo in cui ho costruito la tabella, queste 861 righe saranno distribuite più o meno uniformemente attorno al tavolo. Pertanto, la query:

select * from t1 where object_id is not null; 

è probabilità di visitare quasi ogni blocco in T1 per ottenere i dati, anche se l'ottimizzatore utilizzato l'indice. Ha senso quindi fare a meno dell'indice e fare una scansione completa della tabella!

Una statistica chiave per aiutare a identificare questa situazione è il fattore indice di clustering:

SQL> select clustering_factor from user_indexes where index_name='T1_IDX'; 

CLUSTERING_FACTOR 
----------------- 
       460 

Questo valore 460 è abbastanza alto (rispetto ai 861 righe dell'indice), e suggerisce che una scansione completa della tabella sarà essere usato. Vedi this DBAZine article on clustering factors.

1

Può dipendere dal tipo di indice presente sulla tabella.

La maggior parte degli indici B-tree fa non voci nulle nel negozio. Gli indici bitmap do memorizzano voci nulle.

Quindi, se si dispone di:

select * from mytable dove MyColumn è nullo

e si dispone di un indice B-tree di serie su mycolumn, quindi la query non può utilizza l'indice poiché "null" non è nell'indice.

(Se l'indice è contro più colonne, e una delle colonne indicizzate non è nullo allora ci sarà una voce nell'indice.)

+1

La domanda riguardava una ricerca 'is not null', non' is null'. – KajMagnus

+0

Tuttavia, è utile informazioni –

0

È inoltre degno verificare se le statistiche di Oracle sul tavolo sono fino a Data. Potrebbe non sapere che una scansione completa della tabella sarà più lenta.

0

Oracle Non indicizzare valori nulli a tutti in (B-tree) regolari indici, quindi non può essere utilizzato né si puo' t forza il database Oracle per usarlo.

BR

+0

Questa domanda riguarda 'NON è null'. Tutti i valori rilevanti saranno nell'indice. –

0

L'utilizzo dei suggerimenti deve essere eseguito solo come soluzione anziché come soluzione.

Come menzionato in altre risposte, il valore nullo non è disponibile negli indici B-TREE.

Poiché si sa che in questa colonna sono presenti principalmente valori null, sarebbe possibile sostituire il valore null con un intervallo, ad esempio.

Che in realtà dipende dalla vostra colonna e la natura dei vostri dati, ma in genere, se la vostra colonna è un tipo di data per esempio:

where mydatecolumn is not null può essere tradotto in una regola dicendo: voglio tutte le righe che hanno un Data.

Poi si può sicuramente fare questo: dove mydatecolumn < = sysdate (in Oracle)

Ciò restituirà tutte le righe con una data e ommit valori nulli sfruttando l'indice su quella colonna senza l'uso di suggerimenti.

Problemi correlati