2009-04-06 6 views
15

Nous avons Oracle 10g et nous devons interroger 1 table (pas de jointures) et filtrer les lignes où 1 des colonnes est nulle. Lorsque nous faisons cela - O WH OurColumn n'est pas NULL - nous obtenons un scan de table complet sur une très grande table - BAD BAD BAD. La colonne a un index dessus mais elle est ignorée dans cette instance. Y a-t-il des solutions à cela?Oracle 10g - optimiser O WH NE TROUVE PAS NULL

Merci

Répondre

20

L'optimiseur pense que l'analyse complète de la table sera meilleure.

S'il n'y a que quelques lignes NULL, l'optimiseur a raison.

Si vous êtes absolument sûr que l'accès à l'index sera plus rapide (qui est, vous avez plus de 75% lignes avec col1 IS NULL), puis laisser entendre votre requête:

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

Pourquoi 75%? Parce que l'utilisation de pour récupérer des valeurs non couvertes par l'index implique une jointure cachée sur ROWID, ce qui coûte environ 4 fois autant que le balayage de table.

Si la plage d'index comprend plus de 25% de lignes, l'analyse de table est généralement plus rapide.

Comme mentionné par Tony Andrews, facteur de regroupement est la méthode plus précise pour mesurer cette valeur, mais 25% est toujours une bonne règle.

+1

Quassnoi, où obtenez-vous ce 75%? S'il y a un million de lignes et qu'une seule est nulle, pourquoi utiliser un index sur ces colonnes serait plus lent qu'un balayage de table? – tpdi

+1

Parce que l'index impie une jointure cachée sur ROWID, ce qui coûte environ 4 fois plus que l'analyse de la table. Si la sélectivité de l'index est inférieure à 25%, l'analyse de la table est généralement plus rapide. – Quassnoi

+2

Lors d'une analyse de table complète, vous parcourez simplement toutes les lignes de la table; Si vous effectuez une analyse d'index, vous devez d'abord lire l'index, puis lire la table. À partir d'un certain point, le coût de la lecture d'un index est plus élevé que la simple lecture de l'ensemble du tableau. – andri

2

Si vous faites un select *, il serait logique de faire une analyse de la table plutôt que d'utiliser l'index. Si vous connaissez les colonnes qui vous intéressent, vous pouvez créer un index couvert avec ces colonnes plus celui dans lequel vous appliquez la condition IS NOT NULL.

0

Créez un index sur cette colonne. Pour vous assurer que l'index est utilisé, il doit être sur l'index et les autres colonnes dans le où.

ocdecio répondit:

Si vous faites un select *, il serait logique de faire plutôt une analyse table que l'aide de l'index.

Ce n'est pas strictement vrai; un index sera utilisé s'il existe un index qui correspond à votre clause where, et que l'optimiseur de requêtes décide d'utiliser cet index plus rapidement que de faire une analyse de table. S'il n'y a pas d'index, ou pas d'index approprié, alors seulement une analyse de table doit être faite.

+0

De superbes commentaires sur Select * Juste pour clarifier cependant - nous n'utilisons jamais SELECT * pour d'autres raisons - nous incluons toujours notre liste de colonnes dans les clauses SELECT. –

15

L'optimiseur prendra sa décision en fonction du coût relatif de l'analyse complète de la table et de l'utilisation de l'index. Cela revient principalement à combien de blocs devront être lus pour satisfaire la requête. La règle empirique de 25%/75% mentionnée dans une autre réponse est simpliste: dans certains cas, un balayage de table complet aura du sens même pour obtenir 1% des rangées - c'est-à-dire si ces rangées sont réparties sur plusieurs blocs.

Par exemple, considérez ce tableau:

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  

Comme vous pouvez le voir, seulement environ 1% des lignes T1 ont un object_id non nul.Mais en raison de la façon dont j'ai construit la table, ces 861 lignes seront réparties plus ou moins uniformément autour de la table. Par conséquent, la requête:

select * from t1 where object_id is not null; 

est susceptible de visiter presque chaque bloc dans T1 pour obtenir des données, même si l'optimiseur utilisé l'index. Il est donc logique de se passer de l'index et de faire un scan de table complet!

Une statistique clé pour aider à identifier cette situation est le facteur de regroupement d'index:

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

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

Cette valeur 460 est assez élevé (par rapport aux 861 lignes dans l'index), et suggère qu'un scan de table sera être utilisé. Voir this DBAZine article on clustering factors.

1

Cela peut dépendre du type d'index que vous avez sur la table.

La plupart des index B-tree font et non. Les index bitmap font stocker les entrées null.

Donc, si vous avez:

select * from mytable où mycolumn est nul

et vous avez un index standard B-tree sur mycolumn, la requête ne peut pas utiliser l'index comme le "null" n'est pas dans l'index.

(Si l'indice est contre plusieurs colonnes, et l'une des colonnes indexées n'est pas nul alors il y aura une entrée dans l'indice.)

+1

La question concernait une recherche 'is not null', pas' is null'. – KajMagnus

+0

Néanmoins, il s'agit d'informations utiles –

0

Il est également intéressant de vérifier si les statistiques d'Oracle sur la table sont à rendez-vous amoureux. Il peut ne pas savoir qu'un balayage de table complet sera plus lent.

0

base de données Oracle ne pas indexer les valeurs NULL à tous dans les indices réguliers (b-tree), donc il ne peut pas l'utiliser, ni vous pouvez » t force la base de données Oracle pour l'utiliser.

BR

+0

Cette question concerne 'n'est pas nulle'. Toutes les valeurs pertinentes seront dans l'index. –

0

L'utilisation de repères doit être effectuée uniquement comme solution de contournement plutôt que solution.

Comme mentionné dans les autres réponses, la valeur nulle n'est pas disponible dans les index B-TREE.

Puisque vous savez que vous avez pour la plupart des valeurs nulles dans cette colonne, seriez-vous capable de remplacer la valeur nulle par une plage par exemple.

Cela dépend vraiment de votre colonne et la nature de vos données, mais en général, si votre colonne est un type de date, par exemple:

where mydatecolumn is not null peut être traduit dans une règle disant: Je veux toutes les lignes qui ont un rendez-vous amoureux.

Ensuite, vous pouvez plus faire sans aucun doute ceci: où mydatecolumn < = sysdate (Oracle)

Ceci renverra toutes les lignes avec une date et ommettre valeurs nulles tout en profitant de l'index sur cette colonne sans utiliser astuces.

Questions connexes