Précédent   Forum des professionnels en informatique > Bases de données > Oracle > SQL
SQL Forum d'entraide sur le SQL pour Oracle
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 31/03/2011, 11h41   #1
Membre Expert
 
Avatar de nuke_y
 
Inscription : mai 2004
Messages : 1 812
Détails du profil
Informations forums :
Inscription : mai 2004
Messages : 1 812
Points : 1 609
Points : 1 609
Par défaut [10g] Bug de l'optimiseur Oracle quand il ré-écrit la requête

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 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
WITH
"CLIENT" AS
(
 SELECT '1ER CLIENT' "NAME", 1 "ID" FROM dual
),
"CONTRAT" AS
(
 SELECT '1ER CONTRAT' "NAME", 1 "ID", 1 "ID_CLIENT" FROM dual
 union
 SELECT '2E CONTRAT' "NAME", 2 "ID", 1 "ID_CLIENT" FROM dual
),
"PRESTATION" AS
(
 SELECT '1ER PRESTATION' "NAME", 1 "ID", 1 "ID_CONTRAT", sysdate-3 "DATE" FROM dual
 union
  SELECT '2E PRESTATION' "NAME", 2 "ID", 1 "ID_CONTRAT", sysdate-2 "DATE" FROM dual
  union
   SELECT '3E PRESTATION' "NAME", 3 "ID", 1 "ID_CONTRAT", sysdate-1 "DATE" FROM dual
   union
   SELECT '4E PRESTATION' "NAME", 4 "ID", 2 "ID_CONTRAT", sysdate-3 "DATE" FROM dual
 union
  SELECT '5E PRESTATION' "NAME", 5 "ID", 2 "ID_CONTRAT", sysdate-20 "DATE" FROM dual
  union
   SELECT '6E PRESTATION' "NAME", 6 "ID", 2 "ID_CONTRAT", sysdate-1 "DATE" FROM dual
),
"TARIF" AS
(
 SELECT 1 "ID_CONTRAT", sysdate-20 "DATE_START", sysdate-4 "DATE_END", 1 "TARIF" FROM dual
 union
 SELECT 1 "ID_CONTRAT", sysdate-4 "DATE_START", sysdate "DATE_END", 2 "TARIF" FROM dual
 union
 SELECT 2 "ID_CONTRAT", sysdate-20 "DATE_START", sysdate-4 "DATE_END", 3 "TARIF" FROM dual
 union
 SELECT 2 "ID_CONTRAT", sysdate-4 "DATE_START", sysdate "DATE_END", 4 "TARIF" FROM dual
)
SELECT
*
FROM
"CLIENT"
INNER JOIN "CONTRAT" ON ("CLIENT"."ID" = "CONTRAT"."ID_CLIENT")
INNER JOIN "PRESTATION" ON ("CONTRAT"."ID" = "PRESTATION"."ID_CONTRAT")
LEFT OUTER JOIN "TARIF" ON ("CONTRAT"."ID" = "TARIF"."ID_CONTRAT" AND "PRESTATION"."DATE" BETWEEN "TARIF"."DATE_START" AND "TARIF"."DATE_END")
ORDER BY 2, 4, 7
Code :
1
2
3
4
5
6
7
NAME;ID;NAME_1;ID_1;ID_CLIENT;NAME_2;ID_2;ID_CONTRAT;DATE;ID_CONTRAT_1;DATE_START;DATE_END;TARIF
1ER CLIENT;1;1ER CONTRAT;1;1;1ER PRESTATION;1;1;28/03/2011 09:18:46;1;27/03/2011 09:18:46;31/03/2011 09:18:46;2
1ER CLIENT;1;1ER CONTRAT;1;1;2E PRESTATION;2;1;29/03/2011 09:18:46;1;27/03/2011 09:18:46;31/03/2011 09:18:46;2
1ER CLIENT;1;1ER CONTRAT;1;1;3E PRESTATION;3;1;30/03/2011 09:18:46;1;27/03/2011 09:18:46;31/03/2011 09:18:46;2
1ER CLIENT;1;2E CONTRAT;2;1;4E PRESTATION;4;2;28/03/2011 09:18:46;2;27/03/2011 09:18:46;31/03/2011 09:18:46;4
1ER CLIENT;1;2E CONTRAT;2;1;5E PRESTATION;5;2;11/03/2011 09:18:46;2;11/03/2011 09:18:46;27/03/2011 09:18:46;3
1ER CLIENT;1;2E CONTRAT;2;1;6E PRESTATION;6;2;30/03/2011 09:18:46;2;27/03/2011 09:18:46;31/03/2011 09:18:46;4
Ma problèmatique est de sortir pour chaque client, en 1 seule requête, les contrats et pour celui sur lequel a eu lieu la 1ere prestation du client (chronologiquement et pour tous les contrats confondus) le tarif qui s'est appliqué. Comme il s'agit en fait d'une requête générée par un générateur de requête tel que Business Object, je ne peux pas faire ce que je veux.

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 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
WITH
"CLIENT" AS
(
 SELECT '1ER CLIENT' "NAME", 1 "ID" FROM dual
),
"CONTRAT" AS
(
 SELECT '1ER CONTRAT' "NAME", 1 "ID", 1 "ID_CLIENT" FROM dual
 union
 SELECT '2E CONTRAT' "NAME", 2 "ID", 1 "ID_CLIENT" FROM dual
),
"PRESTATION" AS
(
 SELECT '1ER PRESTATION' "NAME", 1 "ID", 1 "ID_CONTRAT", sysdate-3 "DATE" FROM dual
 union
  SELECT '2E PRESTATION' "NAME", 2 "ID", 1 "ID_CONTRAT", sysdate-2 "DATE" FROM dual
  union
   SELECT '3E PRESTATION' "NAME", 3 "ID", 1 "ID_CONTRAT", sysdate-1 "DATE" FROM dual
   union
   SELECT '4E PRESTATION' "NAME", 4 "ID", 2 "ID_CONTRAT", sysdate-3 "DATE" FROM dual
 union
  SELECT '5E PRESTATION' "NAME", 5 "ID", 2 "ID_CONTRAT", sysdate-20 "DATE" FROM dual
  union
   SELECT '6E PRESTATION' "NAME", 6 "ID", 2 "ID_CONTRAT", sysdate-1 "DATE" FROM dual
),
"TARIF" AS
(
 SELECT 1 "ID_CONTRAT", sysdate-20 "DATE_START", sysdate-4 "DATE_END", 1 "TARIF" FROM dual
 union
 SELECT 1 "ID_CONTRAT", sysdate-4 "DATE_START", sysdate "DATE_END", 2 "TARIF" FROM dual
 union
 SELECT 2 "ID_CONTRAT", sysdate-20 "DATE_START", sysdate-4 "DATE_END", 3 "TARIF" FROM dual
 union
 SELECT 2 "ID_CONTRAT", sysdate-4 "DATE_START", sysdate "DATE_END", 4 "TARIF" FROM dual
)
SELECT   "CLIENT"."NAME" "Client Name",
         "CONTRAT"."NAME" "Contrat Name",
		 "TARIF_FOR_FIRST_PREST"."TARIF" "Tarif",
		 "CONTRAT"."ID",
		 "TARIF_FOR_FIRST_PREST"."ID_CONTRAT"
    FROM 
		 "CLIENT"
		 INNER JOIN "CONTRAT" ON ("CLIENT"."ID" = "CONTRAT"."ID_CLIENT")
		 LEFT OUTER JOIN
         (SELECT
		 		"TARIF"."ID_CONTRAT",
                "TARIF"."TARIF"
            FROM
				"TARIF"
				INNER JOIN
                 (SELECT
				 		"CONTRAT"."ID_CLIENT",
                        min("CONTRAT"."ID") KEEP (DENSE_RANK FIRST ORDER BY "PRESTATION"."DATE" ASC) "ID_ON_FIRST_DATE",
                        min("PRESTATION"."DATE") "FIRST_DATE"
                  FROM
					  	"CONTRAT"
						INNER JOIN "PRESTATION" ON ("CONTRAT"."ID" = "PRESTATION"."ID_CONTRAT")   
                  GROUP BY
				  		"CONTRAT"."ID_CLIENT"
				 )"FIRST_PREST_DT_AND_CONTRACT_ID"
                 ON ( "TARIF"."ID_CONTRAT" = "FIRST_PREST_DT_AND_CONTRACT_ID"."ID_ON_FIRST_DATE" AND "FIRST_PREST_DT_AND_CONTRACT_ID"."FIRST_DATE" BETWEEN "TARIF"."DATE_START" AND "TARIF"."DATE_END")
		 ) "TARIF_FOR_FIRST_PREST" ON ("TARIF_FOR_FIRST_PREST"."ID_CONTRAT" = "CONTRAT"."ID")
   WHERE "CLIENT"."NAME" = ('1ER CLIENT')
ORDER BY 1
qui me donne ce résultat
Code :
1
2
3
Client Name;Contrat Name;Tarif;ID;ID_CONTRAT
1ER CLIENT;2E CONTRAT;3;2;2
1ER CLIENT;1ER CONTRAT;;1;
La 1ere prestation du client 1, qui a eu lieu à sysdate-20, était bien liée au 2e contrat et le tarif qui s'appliquait était bien 3.

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 :
1
2
3
Client Name;Contrat Name;Tarif;ID;ID_CONTRAT
1ER CLIENT;2E CONTRAT;;2;
1ER CLIENT;1ER CONTRAT;;1;
Par contre si je ne restreint pas les résultats à UN client (donc j'enlève WHERE "CLIENT"."NAME" = ('1ER CLIENT'), il trouve bien mes données pour chaque client (dont client 1).
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"
Enfin, si j'abandonne le min("CONTRAT"."ID") KEEP ... et que je fais une sous-requête classique, il trouve bien les données.

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:
10.2.0.4.0 - 64bit
With the Partitioning, OLAP, Data Mining and Real Application Testing options
__________________
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.
nuke_y est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/03/2011, 13h34   #2
Membre chevronné
 
Homme O. Joly
Support
Inscription : décembre 2010
Messages : 287
Détails du profil
Informations personnelles :
Nom : Homme O. Joly
Âge : 38
Localisation : France, Seine et Marne (Île de France)

Informations professionnelles :
Activité : Support
Secteur : High Tech - Éditeur de logiciels

Informations forums :
Inscription : décembre 2010
Messages : 287
Points : 617
Points : 617
Tu peux poster les divers plans d'exécution (en autotrace traceonly) ?
ojo77 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/03/2011, 14h08   #3
Membre Expert
 
Avatar de nuke_y
 
Inscription : mai 2004
Messages : 1 812
Détails du profil
Informations forums :
Inscription : mai 2004
Messages : 1 812
Points : 1 609
Points : 1 609
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 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-----------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                    |     3 |   219 |    41   (8)| 00:00:01 |
|   1 |  NESTED LOOPS OUTER            |                    |     3 |   219 |    41   (8)| 00:00:01 |
|*  2 |   HASH JOIN                    |                    |     1 |    45 |     8  (13)| 00:00:01 |
|   3 |    TABLE ACCESS BY INDEX ROWID | TCLIENT            |     1 |    19 |     2   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN           | IDX_TCLIENT_NAME_1 |     1 |       |     1   (0)| 00:00:01 |
|   5 |    TABLE ACCESS FULL           | TCONTRACTS_CLT     |  1016 | 26416 |     5   (0)| 00:00:01 |
|   6 |   VIEW PUSHED PREDICATE        |                    |     1 |    28 |    33   (7)| 00:00:01 |
|*  7 |    HASH JOIN                   |                    |     1 |    23 |    33   (7)| 00:00:01 |
|   8 |     TABLE ACCESS BY INDEX ROWID| TPRICE_LISTS       |     2 |    20 |     2   (0)| 00:00:01 |
|*  9 |      INDEX RANGE SCAN          | FK_TPRICE_LISTS_01 |     2 |       |     1   (0)| 00:00:01 |
|  10 |     VIEW                       |                    |    15 |   195 |    31   (7)| 00:00:01 |
|* 11 |      FILTER                    |                    |       |       |            |          |
|  12 |       SORT GROUP BY            |                    |    15 |   345 |    31   (7)| 00:00:01 |
|* 13 |        HASH JOIN               |                    |  1454 | 33442 |    30   (4)| 00:00:01 |
|  14 |         TABLE ACCESS FULL      | TCONTRACTS_CLT     |  1016 | 13208 |     5   (0)| 00:00:01 |
|  15 |         TABLE ACCESS FULL      | TPRESTATIONS       |  1474 | 14740 |    24   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------
Le "bon" plan, celui qu'Oracle fait quand je "bloque" l'optimiseur en faisant une jointure avec un trunc(id), et qui renvoie les données justes:
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-----------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                    |    28 |  2044 |    41   (8)| 00:00:01 |
|   1 |  NESTED LOOPS OUTER            |                    |    28 |  2044 |    41   (8)| 00:00:01 |
|*  2 |   HASH JOIN                    |                    |     1 |    45 |     8  (13)| 00:00:01 |
|   3 |    TABLE ACCESS BY INDEX ROWID | TCLIENT            |     1 |    19 |     2   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN           | IDX_TCLIENT_NAME_1 |     1 |       |     1   (0)| 00:00:01 |
|   5 |    TABLE ACCESS FULL           | TCONTRACTS_CLT     |  1016 | 26416 |     5   (0)| 00:00:01 |
|   6 |   VIEW PUSHED PREDICATE        |                    |     1 |    28 |    33   (7)| 00:00:01 |
|*  7 |    HASH JOIN                   |                    |    27 |   621 |    33   (7)| 00:00:01 |
|   8 |     TABLE ACCESS BY INDEX ROWID| TPRICE_LISTS       |     2 |    20 |     2   (0)| 00:00:01 |
|*  9 |      INDEX RANGE SCAN          | FK_TPRICE_LISTS_01 |     2 |       |     1   (0)| 00:00:01 |
|  10 |     VIEW                       |                    |  1454 | 18902 |    31   (7)| 00:00:01 |
|  11 |      SORT GROUP BY             |                    |  1454 | 33442 |    31   (7)| 00:00:01 |
|* 12 |       HASH JOIN                |                    |  1454 | 33442 |    30   (4)| 00:00:01 |
|  13 |        TABLE ACCESS FULL       | TCONTRACTS_CLT     |  1016 | 13208 |     5   (0)| 00:00:01 |
|  14 |        TABLE ACCESS FULL       | TPRESTATIONS       |  1474 | 14740 |    24   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------
Un autre "bon" plan, celui qu'Oracle fait quand je remplace la jointure ouverte par une fermée et qui renvoie des données justes (excepté le fait que pour ma requête finale j'en ai besoin de la jointure ouverte):
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
------------------------------------------------------------------------------------------------------
| Id  | Operation                       | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |                    |     3 |   189 |    41   (8)| 00:00:01 |
|*  1 |  HASH JOIN                      |                    |     3 |   189 |    41   (8)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID   | TPRICE_LISTS       |     2 |    20 |     2   (0)| 00:00:01 |
|   3 |    NESTED LOOPS                 |                    |     2 |   100 |    10  (10)| 00:00:01 |
|*  4 |     HASH JOIN                   |                    |     1 |    40 |     8  (13)| 00:00:01 |
|   5 |      TABLE ACCESS BY INDEX ROWID| TCLIENT            |     1 |    19 |     2   (0)| 00:00:01 |
|*  6 |       INDEX RANGE SCAN          | IDX_TCLIENT_NAME_1 |     1 |       |     1   (0)| 00:00:01 |
|   7 |      TABLE ACCESS FULL          | TCONTRACTS_CLT     |  1016 | 21336 |     5   (0)| 00:00:01 |
|*  8 |     INDEX RANGE SCAN            | FK_TPRICE_LISTS_01 |     2 |       |     1   (0)| 00:00:01 |
|   9 |   VIEW                          |                    |  1454 | 18902 |    31   (7)| 00:00:01 |
|  10 |    HASH GROUP BY                |                    |  1454 | 33442 |    31   (7)| 00:00:01 |
|* 11 |     HASH JOIN                   |                    |  1454 | 33442 |    30   (4)| 00:00:01 |
|  12 |      TABLE ACCESS FULL          | TCONTRACTS_CLT     |  1016 | 13208 |     5   (0)| 00:00:01 |
|  13 |      TABLE ACCESS FULL          | TPRESTATIONS       |  1474 | 14740 |    24   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------
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.
nuke_y est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/03/2011, 14h12   #4
Expert Confirmé Sénior
 
Avatar de mnitu
 
Homme Marius Nitu
Ingénieur développement logiciels
Inscription : octobre 2007
Messages : 3 311
Détails du profil
Informations personnelles :
Nom : Homme Marius Nitu
Localisation : France, Marne (Champagne Ardenne)

Informations professionnelles :
Activité : Ingénieur développement logiciels
Secteur : High Tech - Éditeur de logiciels

Informations forums :
Inscription : octobre 2007
Messages : 3 311
Points : 5 808
Points : 5 808
Faitez vos jointures des tables naturellement et appliquez Min… Keep … avec Group BY sur la jointure. Quelque chose de type
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 
WITH
"CLIENT" AS
(
 SELECT '1ER CLIENT' "NAME", 1 "ID" FROM dual
),
"CONTRAT" AS
(
 SELECT '1ER CONTRAT' "NAME", 1 "ID", 1 "ID_CLIENT" FROM dual
 union
 SELECT '2E CONTRAT' "NAME", 2 "ID", 1 "ID_CLIENT" FROM dual
),
"PRESTATION" AS
(
 SELECT '1ER PRESTATION' "NAME", 1 "ID", 1 "ID_CONTRAT", sysdate-3 "DATE" FROM dual
 union
  SELECT '2E PRESTATION' "NAME", 2 "ID", 1 "ID_CONTRAT", sysdate-2 "DATE" FROM dual
  union
   SELECT '3E PRESTATION' "NAME", 3 "ID", 1 "ID_CONTRAT", sysdate-1 "DATE" FROM dual
   union
   SELECT '4E PRESTATION' "NAME", 4 "ID", 2 "ID_CONTRAT", sysdate-3 "DATE" FROM dual
 union
  SELECT '5E PRESTATION' "NAME", 5 "ID", 2 "ID_CONTRAT", sysdate-20 "DATE" FROM dual
  union
   SELECT '6E PRESTATION' "NAME", 6 "ID", 2 "ID_CONTRAT", sysdate-1 "DATE" FROM dual
),
"TARIF" AS
(
 SELECT 1 "ID_CONTRAT", sysdate-20 "DATE_START", sysdate-4 "DATE_END", 1 "TARIF" FROM dual
 union
 SELECT 1 "ID_CONTRAT", sysdate-4 "DATE_START", sysdate "DATE_END", 2 "TARIF" FROM dual
 union
 SELECT 2 "ID_CONTRAT", sysdate-20 "DATE_START", sysdate-4 "DATE_END", 3 "TARIF" FROM dual
 union
 SELECT 2 "ID_CONTRAT", sysdate-4 "DATE_START", sysdate "DATE_END", 4 "TARIF" FROM dual
)
SELECT Min("CLIENT"."NAME") KEEP (DENSE_RANK FIRST ORDER BY "PRESTATION"."DATE" ASC ) "Client Name",
       Min("CONTRAT"."NAME") KEEP (DENSE_RANK FIRST ORDER BY "PRESTATION"."DATE" ASC ) "Contrat Name",
		   Min("TARIF"."TARIF") KEEP (DENSE_RANK FIRST ORDER BY "PRESTATION"."DATE" ASC ) "Tarif",
		   "CONTRAT"."ID",
		   Min("TARIF"."ID_CONTRAT") KEEP (DENSE_RANK FIRST ORDER BY "PRESTATION"."DATE" ASC )
FROM
"CLIENT"
JOIN "CONTRAT" ON ("CLIENT"."ID" = "CONTRAT"."ID_CLIENT")
JOIN "PRESTATION" ON ("CONTRAT"."ID" = "PRESTATION"."ID_CONTRAT")
LEFT OUTER JOIN "TARIF" ON ("CONTRAT"."ID" = "TARIF"."ID_CONTRAT" AND "PRESTATION"."DATE" BETWEEN "TARIF"."DATE_START" AND "TARIF"."DATE_END")
GROUP BY "CONTRAT"."ID"
mnitu est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 31/03/2011, 14h48   #5
Membre Expert
 
Avatar de nuke_y
 
Inscription : mai 2004
Messages : 1 812
Détails du profil
Informations forums :
Inscription : mai 2004
Messages : 1 812
Points : 1 609
Points : 1 609
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.
nuke_y est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/03/2011, 15h31   #6
Expert Confirmé Sénior
 
Avatar de mnitu
 
Homme Marius Nitu
Ingénieur développement logiciels
Inscription : octobre 2007
Messages : 3 311
Détails du profil
Informations personnelles :
Nom : Homme Marius Nitu
Localisation : France, Marne (Champagne Ardenne)

Informations professionnelles :
Activité : Ingénieur développement logiciels
Secteur : High Tech - Éditeur de logiciels

Informations forums :
Inscription : octobre 2007
Messages : 3 311
Points : 5 808
Points : 5 808
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.
mnitu est déconnecté   Envoyer un message privé Réponse avec citation 20
Vieux 01/04/2011, 16h34   #7
Membre Expert
 
Avatar de nuke_y
 
Inscription : mai 2004
Messages : 1 812
Détails du profil
Informations forums :
Inscription : mai 2004
Messages : 1 812
Points : 1 609
Points : 1 609
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.
nuke_y est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 01/04/2011, 17h07   #8
Expert Confirmé Sénior
 
Avatar de mnitu
 
Homme Marius Nitu
Ingénieur développement logiciels
Inscription : octobre 2007
Messages : 3 311
Détails du profil
Informations personnelles :
Nom : Homme Marius Nitu
Localisation : France, Marne (Champagne Ardenne)

Informations professionnelles :
Activité : Ingénieur développement logiciels
Secteur : High Tech - Éditeur de logiciels

Informations forums :
Inscription : octobre 2007
Messages : 3 311
Points : 5 808
Points : 5 808
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.
mnitu est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 03h49.


 
 
 
 
Partenaires

Hébergement Web