|
Publicité ' | |||||||||||||||||||||||
|
|
#1 | |||||||||||
|
Membre Expert
![]() Inscription : mai 2004 Messages : 1 812 ![]() |
Bonjour à tous,
je rencontre ce que je considère être un bug de l'optimiseur Oracle sur une 10g. Je voudrais savoir si pour vous j'ai fait une requête horrible qui plante naturellement Oracle ou si ça devrait marcher (même si on peut sûrement l'écrire mieux). Donc j'ai 4 tables: CLIENT contient le client, qui ne varie pas. CONTRAT contient des contrats de prestation de service. Elle est liée à CLIENT en référençant l'ID de client. Il y a de 1 à 4 contrats par client dans la base mais il n'en existe jamais plus d'1 à la fois. Donc quand 1 contrat démarre, le précédent a été fermé (mais pour cet exemple on ne va pas s'en pré-occuper). PRESTATION contient des prestations qui ont lieu à une date donnée. Elle est liée à CONTRAT en référençant l'ID de CONTRAT. Il y a de 1 à 4 prestations par contrat dans la base. TARIF contient des tarifs définis sur des périodes pour chaque contrat. Elle est liée à CONTRAT en référençant l'ID de CONTRAT et il faut préciser 1 date pour trouver le tarif qui s'applique sur 1 contrat à 1 date. Il y a de 1 à 10 tarifs (et donc intervales de tarifs) par contrat. Fonctionnellement il s'agit donc de clients qui passent des contrats pour des prestations, avec des tarifs qui peuvent varier dans le temps. Quand ils bénéficient d'une prestation, à une date donnée c'est le tarif lié à leur contrat à la date de la prestation qui s'applique. La requête suivante sort la liste des clients, contrats, prestations et tarifs Code :
Code :
Bon gré mal gré je suis arrivé à faire ça: je calcule en 1 seule sous-requête, pour chaque client, la date de 1ere prestation et le contrat qui y correspond (en utilisant min("CONTRAT"."ID") KEEP (DENSE_RANK FIRST ORDER BY "PRESTATION"."DATE" ASC) ) et je joint cette "vue" contruite à la volée à ma table des tarifs pour en sortir le tarif du contrat (TARIF_FOR_FIRST_PREST), que je lie ensuite aux contrats et aux clients. Encore une fois, oui on peut faire sûrement mieux, mais je travaille sur un générateur de code, les volumes sont faibles, et c'est plus maintenable parce que je peux réutiliser le bout de code à plusieurs endroits. Code :
Code :
Donc là ça marche très bien. Mon problème c'est que si je remplace mes tables par de vraies tables, la requête me donne ce résultat: Code :
Donc il ne trouve pas de tarif associé si je limite à 1 client. Mais si je remplace left outer join entre mes contrats et ma "vue" TARIF_FOR_FIRST_PREST par une inner join, il trouve bien mes données. Pire, si je garde la jointure ouverte et que j'utilise un trunc sur l'ID pour "bloquer" l'optimiseur, il trouve bien mes données: Code :
ON (trunc("TARIF_FOR_FIRST_PREST"."ID_CONTRAT") = "CONTRAT"."ID" Ma conclusion est donc que, pour optimiser, Oracle ré-écrit ma requête et se trompe sur la "vue" où il y a des min("CONTRAT"."ID") KEEP ..., et que pour "désactiver" l'optimiseur il faut ajouter quelque chose pour l'empêcher de joindre les index entre eux, un trunc sur l'ID par exemple. Pour moi il s'agit clairement d'un bug, et je vais faire avec. Mais je voulais avoir votre avis sur ma requête et savoir si elle était vraiment trop crade, et donc si c'est bien fait pour moi d'avoir ce problème, ou si elle est tolérable et donc Oracle mérite la corde. Merci Citation:
__________________
Il vaut mieux monopoliser son intelligence sur des bêtises que sa bêtise sur des choses intelligentes. Mon combat pour les droits des consommateurs face aux abus des grandes marques. |
|||||||||||
|
|
00
|
|
|
#2 |
|
Membre chevronné
![]() O. JolySupport Inscription : décembre 2010 Messages : 287 ![]() |
Tu peux poster les divers plans d'exécution (en autotrace traceonly) ?
|
|
00
|
|
|
#3 | ||||||
|
Membre Expert
![]() Inscription : mai 2004 Messages : 1 812 ![]() |
Bien sûr. Voici les plans d'exécutions avec les vraies tables:
Le "mauvais" plan, celui qu'Oracle fait par défaut avec la requête de base, et qui renvoie les données fausses: Code :
Code :
Code :
__________________
Il vaut mieux monopoliser son intelligence sur des bêtises que sa bêtise sur des choses intelligentes. Mon combat pour les droits des consommateurs face aux abus des grandes marques. |
||||||
|
|
00
|
|
|
#4 | ||
|
Expert Confirmé Sénior
![]() ![]() Marius NituIngénieur développement logiciels Inscription : octobre 2007 Messages : 3 311 ![]() |
Faitez vos jointures des tables naturellement et appliquez Min… Keep … avec Group BY sur la jointure. Quelque chose de type
Code :
|
||
|
|
10
|
|
|
#5 |
|
Membre Expert
![]() Inscription : mai 2004 Messages : 1 812 ![]() |
Merci mnitu. Alors pour la science j'ai appliqué cette méthode et techniquement ça marche, je n'en doutais pas. Excepté que fonctionnellement je perds le fait de pouvoir repérer sur quel contrat la première prestation a eu lieu, or c'est la moitié de l'intérêt de mon truc.
De plus le fait de faire des min keep... au niveau le plus élevé de la requête est inapplicable avec mon générateur de requête, je dois donc garder une logique: "CLIENT" -jointure simple- "CONTRAT" -jointure simple ouvert- "une vue qui sort le tarif pour le contrat client qui inclu la première prestation" Bon de toutes façons, je ne doute pas qu'on puisse écrire la requête de plusieurs manières différentes, moi-même je ne l'aurai pas naturellement écrite comme ça. Non ce qui me turlupine c'est ce bug. A défaut d'identifier quel patch le corrige, j'aurai voulu avoir votre avis de pro expérimentés sur la "saleté" de la requête. En gros une sorte de sondage: 1) Pas de chance, c'est un sale bug, tu ne pouvais pas prévoir 2) Tu l'as bien mérité, c'était prévisible qu'un truc pareil te saute à la figure Merci
__________________
Il vaut mieux monopoliser son intelligence sur des bêtises que sa bêtise sur des choses intelligentes. Mon combat pour les droits des consommateurs face aux abus des grandes marques. |
|
|
00
|
|
|
#6 |
|
Expert Confirmé Sénior
![]() ![]() Marius NituIngénieur développement logiciels Inscription : octobre 2007 Messages : 3 311 ![]() |
Cachez tout ça derrière une vue pour votre générateur de requête.
C'est un bug si vous arrivez à fournir un jeu d'essai qui démontre vos propos. Sinon ça peut être n’importe quoi, la requête, des données corrompues, des erreurs humaine, etc. |
|
|
20
|
|
|
#7 |
|
Membre Expert
![]() Inscription : mai 2004 Messages : 1 812 ![]() |
Merci.
Malheureusement je ne peux pas créer de vue, ça aurait sûrement résolu mon problème. Je vais partir de la manière dont je suis parti et demander aux DBA de contrôler s'il s'agit d'un bug ou pas. Merci
__________________
Il vaut mieux monopoliser son intelligence sur des bêtises que sa bêtise sur des choses intelligentes. Mon combat pour les droits des consommateurs face aux abus des grandes marques. |
|
|
00
|
|
|
#8 |
|
Expert Confirmé Sénior
![]() ![]() Marius NituIngénieur développement logiciels Inscription : octobre 2007 Messages : 3 311 ![]() |
J’ai un mauvais souvenir avec une base Oracle 9R2 c’est vrai et une requête similaire (sous-requête avec Keep) ou l’optimiseur n’arrivait pas à faire le predicate pushing dans la sous-requête et par conséquence les indexes n’était pas utilisés, bref un désastre dpv performance, que j’ai résolu en appliquant le Keep sur la jointure comme je vous l’ai suggère.
Si vous trouvez que c’est vraiment un bug faite nous un retour. |
|
|
00
|
Copyright © 2000-2012 - www.developpez.com