2009-06-02 4 views
1

DatabaseOracle optimisation de calcul de la date de requête impliquant

Table1 
Id 
Table2Id 

... 

Table2 
    Id 
    StartTime 
    Duration //in hours 

Recherche

select * from Table1 join Table2 on Table2Id = Table2.Id 
where starttime < :starttime and starttime + Duration/24 > :endtime 

Cette requête prend actuellement environ 2 secondes pour exécuter ce qui est trop long. Il y a un index sur les colonnes id et un index de fonction sur Start_time + duration/24 Dans Sql Developer, le plan de requête ne montre aucun index utilisé. La requête renvoie 475 lignes pour mes heures de début et de fin de test. Table2 a ~ 800k lignes Table1 a ~ 200k lignes

Si le calcul durée/24 est supprimé de la requête, remplacé par une valeur statique, le temps de requête est réduit de moitié. Cela ne récupère pas exactement les mêmes données, mais me laisse croire que la division est chère.

J'ai également testé l'ajout d'une colonne Endtime à Table2 qui est remplie avec (starttime + duration/24) La colonne était pré-remplie via une seule mise à jour, si elle était utilisée en production.

select * from Table1 join Table2 on Table2Id = Table2.Id 
where starttime < :starttime and endtime > :endtime 

Cette requête s'exécutera dans environ 600ms et utilisera un index pour la jointure. C'est moins qu'idéal en raison de la colonne supplémentaire avec des données redondantes.

Existe-t-il des méthodes pour rendre cette requête plus rapide?

+0

D'après les requêtes affichées, vous récupérez tous les enregistrements du tableau 2 qui ne se trouvent PAS dans une plage donnée. Odd, mais d'accord. Vos statistiques sont-elles à jour? Peut-être que l'optimiseur a des informations erronées et qu'il choisit un mauvais plan? Quel problème essayez-vous de résoudre? Avez-vous besoin des données de Table2, ou est-ce que tout ce dont vous avez besoin dans le Tableau 1? Un commentaire sur la distribution des données? Version Oracle (il peut y avoir une fonctionnalité que vous pouvez/ne pouvez pas utiliser pour aider)? –

+0

Les stats Oracle 10g sont à jour. J'ai eu la jointure en arrière, mauvaise traduction des vraies tables. –

+0

Avez-vous au moins exécuté SQL * Plus AUTOTRACE, ou un EXPLAIN PLAN (mieux encore, l'événement de traçage 10046.) Pour régler une requête, il est important de savoir d'abord ce que fait Oracle. – spencer7593

Répondre

3

créer un index de fonction à la fois starttime et l'expression starttime + Duration/24:

create index myindex on table2(starttime, starttime + Duration/24); 

doit être sélectionné Un indice composé sur le prédicat ensemble de votre requête, alors indexé individuellement l'optimiseur décide probable que la table répétée accès par rowid basé sur une analyse de l'un de ces index est en fait plus lent qu'un balayage de table complet.

Assurez-vous également que vous n'effectuez pas de conversion implicite de varchar à date, en vous assurant que vous transmettez des DATE dans vos variables de liaison.

Essayez d'abaisser le paramètre système optimizer_index_cost_adj. Je crois que la valeur par défaut est 100. Essayez de définir cela à 10 et voir si votre index est sélectionné.

Envisagez de partitionner la table au démarrage.

+0

Comme je l'ai mentionné dans la question, cet index existe déjà. Les dates sont transmises en tant que dates sur la requête paramétrée. Je vais regarder le optimizer_index_cost_adj –

+2

Cet index composé existe-t-il? – Apocalisp

+0

Nous nous attendons à ce qu'un index basé sur une fonction composite (comme suggéré par Apocalisp) soit un candidat idéal pour la requête. (Toutes les suggestions normales s'appliquent ici: statistiques à jour, PLAN EXPLAIN, SQL * Plus AUTOTRACE, trace d'événement 10046, trace d'événement 10053). Bonne astuce sur la possible conversion de données implicite. (Je passe normalement tous les arguments bind en tant que chaînes, et je fais la conversion explicite dans l'instruction ... startime> = TO_DATE (: b1, 'YYYYMMDDHH24MISS') ... plutôt que de laisser Oracle (SQL Developer ne montre pas l'index utilisé, SQL Developer voit les arguments de liaison comme type de données DATE? – spencer7593

1

Oracle n'utilise pas d'index si la sélectivité de la clause where n'est pas très bonne. L'index serait utilisé si le nombre de lignes renvoyées correspondait à un certain pourcentage du nombre total de lignes dans le tableau (le pourcentage varie, car Oracle comptabilise le coût de lecture de l'index ainsi que la lecture des tables).

De même, lorsque les colonnes d'index sont modifiées dans la clause where, l'index est désactivé. Par exemple, UPPERCASE (some_index_column), désactiverait l'utilisation de l'index sur some_index_column. C'est pourquoi starttime + Duration/24>: endtime n'utilise pas l'index.

Pouvez-vous essayer

select * from Table1 join Table2 on Table1.Id = Table2.Table1Id 
where starttime < :starttime and starttime > :endtime - Duration/24 

Cela devrait permettre l'utilisation de l'indice et il n'y a pas besoin d'une colonne supplémentaire.

+1

qui supprime 150-200ms de l'heure de la requête et utilise l'index pour la jointure. Mieux mais pas génial. –

+0

-1 en raison de la remarque de 5% et le "je me sens" sans test, mais surtout le 5%, ce qui est un non-sens. –

+0

Puis-je savoir ce que vous pensez que 5% est un non-sens? La littérature suggère que la sélectivité devrait être d'environ 5% à 20%. La valeur exacte dépend de la base de données. Regardez ce post, où la sélectivité de 15% est mentionnée. http: // stackoverflow.com/questions/212264/comment-choisir-et-optimiser-oracle-index – Sathya

1

Vous avez deux critères avec des prédicats de plage (supérieur à/inférieur à).Une analyse de plage d'index peut commencer à un point de l'index et se terminer à un autre. Pour un index composé au démarrage et "Starttime + duration/24", puisque la colonne principale est starttime et que le prédicat est "less than bind value", il commencera au bord le plus à gauche de l'index (début le plus tôt).) et la plage balaie toutes les lignes jusqu'au point où l'heure de début atteint la limite. Pour chacune de ces correspondances, il peut évaluer la valeur calculée pour "Starttime + duration/24" sur l'index par rapport à la valeur de liaison et transmettre ou rejeter la ligne. Je soupçonne que la plupart des données de la table sont anciennes, donc la plupart des entrées ont un ancien début et vous finirez par scanner la plus grande partie de l'index. Pour un index composé sur "Starttime + durée/24" et starttime, puisque la colonne principale est la fonction et que le prédicat est "supérieur à bindvalue", il commencera à mi-chemin à travers l'index et avancera jusqu'à la fin . Pour chacune de ces correspondances, il peut évaluer l'heure de début de l'index par rapport à la valeur de liaison et transmettre ou rejeter la ligne. Si la date de fin passée est récente, je suppose que cela impliquerait une quantité beaucoup plus petite de l'index analysé.

Même sans l'heure de début en tant que deuxième colonne de l'index, l'index de fonction existant sur "Starttime + durée/24" doit toujours être utile et utilisé. Vérifiez le plan d'explication pour vous assurer que la valeur de liaison est une date ou convertie en une date. Si elle est convertie, assurez-vous que le masque de format approprié est utilisé (par exemple, une valeur entrée '1/Jun/09' peut être convertie en année 0009, Oracle verra la condition très détendue et aura tendance à ne pas utiliser l'index - plus le résultat pourrait être faux). "Dans le développeur Sql, le plan de requête ne montre aucun index utilisé." Si l'index n'était pas utilisé pour trouver les lignes table2, je soupçonne que l'optimiseur pensait que la totalité/table2 serait retournée [ce qui est évident 't, par vos chiffres]. J'imagine que la plus grande partie de table1 serait retournée, et donc aucun de vos prédicats n'a fait beaucoup de filtrage. Comme je l'ai dit plus haut, je pense que le prédicat «inférieur à» n'est pas sélectif, mais que le «plus grand que» devrait l'être. Regardez le plan d'expliquer, en particulier la valeur ROWS, pour voir ce que Oracle pense

PS. L'ajustement de la valeur signifie que l'optimiseur modifie la base de ses estimations. Si un planificateur de voyage dit que vous prendrez six heures pour un voyage parce qu'il suppose une vitesse moyenne de 50, si vous lui dites de supposer une moyenne de 100 il sortira avec trois heures. cela n'affectera pas réellement la vitesse à laquelle vous voyagez, ni le temps nécessaire pour faire le voyage. Vous ne voulez donc que modifier cette valeur pour qu'elle reflète plus précisément la valeur réelle de votre base de données (ou session).