2009-02-26 8 views
4

La requête:Pourquoi cette requête effectue-t-elle une analyse de table complète?

SELECT tbl1.* 
    FROM tbl1 
JOIN tbl2 
    ON (tbl1.t1_pk = tbl2.t2_fk_t1_pk 
AND tbl2.t2_strt_dt <= sysdate 
AND tbl2.t2_end_dt >= sysdate) 
JOIN tbl3 on (tbl3.t3_pk = tbl2.t2_fk_t3_pk 
AND tbl3.t3_lkup_1 = 2577304 
AND tbl3.t3_lkup_2 = 1220833) 
where tbl2.t2_lkup_1 = 1020000002981587; 

Faits:

  • Oracle XE
  • tbl1.t1_pk est une clé primaire.
  • tbl2.t2_fk_t1_pk est une clé étrangère sur cette colonne t1_pk.
  • tbl2.t2_lkup_1 est indexé.
  • tbl3.t3_pk est une clé primaire.
  • tbl2.t2_fk_t3_pk est une clé étrangère sur cette colonne t3_pk.

Expliquer le plan sur une base de données avec 11.000 lignes tbl1 et 3500 lignes tbl2 montre qu'il est fait un scan de table sur tbl1. Semble à moi que il devrait être plus rapide s'il pourrait faire une requête d'index sur tbl1. Expliquer le plan sur une base de données avec 11 000 lignes dans tbl1 et 3500 lignes dans tbl2 montre qu'il effectue une analyse de table complète sur tbl1. Semble à moi que il devrait être plus rapide s'il pourrait faire une requête d'index sur tbl1.

Mise à jour: J'ai essayé l'indice que quelques-uns d'entre vous ont suggéré, et le coût de l'explication a été bien pire! Maintenant, je suis vraiment confus.

nouvelle mise à jour: J'ai finalement eu accès à une copie de la base de données de production, et « expliquer le plan » montré à l'aide d'index et une requête de coût beaucoup plus faible. Je suppose que d'avoir plus de données (plus de 100 000 lignes dans tbl1 et 50 000 lignes dans tbl2) étaient ce qu'il fallait pour lui faire décider que les index valaient la peine. Merci à tous ceux qui ont aidé. Je pense toujours que le réglage des performances d'Oracle est un art noir, mais je suis content que certains d'entre vous le comprennent.

Mise à jour supplémentaire: J'ai mis à jour la question à la demande de mon ancien employeur. Ils n'aiment pas que leurs noms de table apparaissent dans les requêtes Google. J'aurais du être mieux informé.

+0

Est-ce une sorte de sadique en choisissant les noms des tables et des colonnes? Ou est-ce le résultat d'un code obfuscator? –

+0

Ce n'est pas vraiment un plan d'explication "complet". AutoTrace dans SQLPlus donne beaucoup plus d'informations. –

+0

Ce sont quelques-uns des pires noms de table que j'ai vu depuis un moment. – Kibbee

Répondre

3

Il serait utile de voir les estimations du nombre de lignes de l'optimiseur, qui ne figurent pas dans la sortie SQL Developer que vous avez publiée.

Je remarque que les deux recherches d'index effectuées sont RANGE SCAN et non UNIQUE SCAN. Donc, ses estimations du nombre de lignes retournées pourraient facilement être loin (que les statistiques soient à jour ou non). Je pense que son estimation du nombre final de lignes du TABLE ACCESS de TBL2 est assez élevé, donc il pense qu'il trouvera un grand nombre de correspondances dans TBL1 et donc décidera de faire une jointure complète de scan/hash plutôt qu'une analyse de boucle/index imbriquée. Pour un vrai plaisir, vous pouvez exécuter la requête avec l'événement 10053 activé et obtenir une trace montrant les calculs effectués par l'optimiseur.

+0

Dave doit être un sacrément bon accordeur, il a écrit tout ce que j'allais faire et plus encore. ;-) Sérieusement, nous avons besoin de la colonne de cardinalité. Utilisez DBMS_XPLAN pour nous donner quelque chose d'utile. –

1

Vous pouvez uniquement le déterminer en consultant le plan de requête créé par l'optimiseur/exécuteur SQL. Il sera au moins partiel basé sur des statistiques d'index qui ne peuvent pas être prédites uniquement à partir de la définition (et peuvent donc changer au fil du temps).

SQL Management Studio pour SQL Server 2005/2008, Analyseur de requêtes pour les versions antérieures.

(Ne peut pas rappeler les noms d'outils à droite pour Oracle.)

5

La réponse facile: Parce que l'optimiseur attend plus de lignes à trouver alors il ne fait trouver.

Consultez les statistiques, elles sont à jour? Vérifiez la cardinalité attendue dans le plan d'explication. Correspondent-ils aux résultats réels? Si non, corrigez les statistiques pertinentes pour cette étape.

Les histogrammes pour les colonnes jointes peuvent aider. Oracle les utilisera pour estimer la cardinalité résultant d'une jointure.

Bien sûr, vous pouvez toujours forcer l'utilisation des index avec un soupçon

1

Essayez d'ajouter un indicateur d'index.

SELECT /*+ index(tbl1 tbl1_index_name) */ ..... 

Parfois, Oracle ne sait pas quel index utiliser.

+0

C'est du code, ça n'abandonne pas parce que je ne sais pas laquelle choisir! il choisit de ne pas utiliser l'index parce que le coût de le faire était plus élevé que le coût de faire autre chose. –

+0

Alors ... donner des chocolats Oracle n'était pas une bonne idée? – Barry

0

Apparemment, cette requête donne au même plan:

SELECT tbl1.* 
FROM tbl1 
JOIN tbl2 ON (tbl1.t1_pk = tbl2.t2_fk_t1_pk) 
JOIN tbl3 on (tbl3.t3_pk = tbl2.t2_fk_t3_pk) 
where tbl2.t2_lkup_1 = 1020000002981587 
AND tbl2.t2_strt_dt <= sysdate 
AND tbl2.t2_end_dt >= sysdate 
AND tbl3.t3_lkup_1 = 2577304 
AND tbl3.t3_lkup_2 = 1220833; 

Qu'est-ce qui se passe si vous réécrivez cette requête:

SELECT tbl1.*  
FROM tbl1 
,  tbl2 
,  tbl3 
where tbl2.t2_lkup_1 = 1020000002981587 
AND tbl1.t1_pk = tbl2.t2_fk_t1_pk 
AND tbl3.t3_pk = tbl2.t2_fk_t3_pk 
AND tbl2.t2_strt_dt <= sysdate 
AND tbl2.t2_end_dt >= sysdate 
AND tbl3.t3_lkup_1 = 2577304 
AND tbl3.t3_lkup_2 = 1220833; 
+0

En fait, la sortie du plan Explain est exactement la même. –

+0

Votre deuxième exemple est ce à quoi ressemblait la requête lorsque j'ai démarré. Je l'ai changé pour faire des jointures, mais cela n'a pas changé le plan d'explication. –

+0

Oracle les transforme dans le même format interne intermédiaire de toute façon. Donc, à moins qu'il y ait un bug avec l'analyse, le plan sera le même. –

2

Oracle tente de retourner le jeu de résultats avec le moins d'E/S requis (généralement, ce qui est logique car les E/S sont lentes). Les index prennent au moins 2 appels d'E/S. un à l'index et un à la table. Habituellement plus, selon la taille de l'index et des tailles de tables et le nombre de retours d'enregistrements, où ils sont dans le fichier de données, ...

C'est où les statistiques entrent en jeu. Disons que votre requête est estimée à retourner 10 enregistrements. L'optimiseur peut calculer que l'utilisation d'un index prendra 10 appels d'E/S. Disons que votre table, selon les statistiques, se trouve dans 6 blocs dans le fichier de données. Il sera plus rapide pour Oracle de faire un scan complet (6 E/S) puis de lire l'index, lire la table, lire puis indexer pour la prochaine clé correspondante, lire la table et ainsi de suite. Par conséquent, dans votre cas, la table peut être très petite. Les statistiques peuvent être désactivées.

J'utilise les éléments suivants pour recueillir des statistiques et le personnaliser pour mes besoins exacts:

begin 

DBMS_STATS.GATHER_TABLE_STATS(ownname 
=> '&owner' ,tabname => '&table_name', estimate_percent => dbms_stats.AUTO_SAMPLE_SIZE,granularity 
=> 'ALL', cascade => TRUE); 

-- DBMS_STATS.GATHER_TABLE_STATS(ownname 
=> '&owner' ,tabname => '&table_name',partname => '&partion_name',granularity => 'PARTITION', estimate_percent => dbms_stats.AUTO_SAMPLE_SIZE, cascade 
=> TRUE); 

-- DBMS_STATS.GATHER_TABLE_STATS(ownname 
=> '&owner' ,tabname => '&table_name',partname => '&partion_name',granularity => 'PARTITION', estimate_percent => dbms_stats.AUTO_SAMPLE_SIZE, cascade 
=> TRUE,method_opt => 'for all indexed columns size 254'); 

end; 
0

Il ressemble à un index de table tbl1 n'est pas ramassée. Assurez-vous que vous avez un index pour la colonne t2_lkup_1 et il ne devrait pas être multi-colonne sinon l'index ne s'applique pas.

(en plus de ce commentaire de Matt) de votre recherche Je crois que vous joindre parce que vous voulez filtrer les enregistrements de ne pas faire JOIN qui peut augmenter cardinalité pour jeu de résultats de la table TBL1 s'il y a des matchs en double de .Voir Jeff Atwood comment

Essayez ce qui utilise existe fonction et rejoindre (ce qui est très rapide sur oracle)

 
select * 
    from tbl1 
where tbl2.t2_lkup_1 = 1020000002981587 and 
     exists (
     select * 
      from tbl2, tbl3 
      where tbl2.t2_fk_t1_pk = tbl1.t1_pk and 
       tbl2.t2_fk_t3_pk = tbl3.t3_pk and 
       sysdate between tbl2.t2_strt_dt and tbl2.t2_end_dt and 
       tbl3.t3_lkup_1 = 2577304 and 
       tbl3.t3_lkup_2 = 1220833); 

0

dépend de votre taille de résultat attendu, vous pouvez jouer arround avec quelques paramètres de session:

SHOW PARAMETER optimizer_index_cost_adj; 
[...] 
ALTER SESSION SET optimizer_index_cost_adj = 10; 

SHOW PARAMETER OPTIMIZER_MODE; 
[...] 
ALTER SESSION SET OPTIMIZER_MODE=FIRST_ROWS_100; 

et ne pas oublier de vérifier l'exécution réelle, parfois le plan n'est pas le monde réel;)

Questions connexes