Requête externe sur résultat de requête externe

Requête externe sur résultat de requête externe - SQL/NoSQL - Programmation

Marsh Posté le 16-09-2008 à 17:26:35    

Salut à tous,
Je fais face (courageusement) au problème de construction de requête suivant. Je n'ai rien trouvé sur le faux-rhum qui pourrait m'aiguiller alors toute aide à ce sujet sera appréciée à sa juste valeur!
 
J'ai 3 tables A, B, C:
A contient des identifiants, C des données, B est une table d'association de A vers C.
J'ai donc une structure qui ressemble à:
 
A
ID=1
ID=2
ID=3
 
B
ID=1  NUM=4
ID=2  NUM=5
ID=2  NUM=6
ID=3  NUM=4
 
C
NUM=4  VAL=7
NUM=5  VAL=8
 
Mon but est de récupérer les données VAL de C relatives au ID 2 de A qui ont un C.VAL=8 (!)  
Je voudrais donc récupérer cette unique ligne:


ID  VAL
2   8


J'écris donc quelque chose comme ça (je veux éviter autant que possible l'utilisation de sous-requêtes pour des raisons de perfs):

Code :
  1. SELECT a.ID, c.VAL
  2. FROM A a, B b, C c
  3. WHERE a.ID = 2
  4. AND a.ID = b.ID(+)
  5. AND b.NUM = c.NUM(+)
  6. AND c.VAL(+) = 8


Pourtant, je récupère 4 résultats, soit 2^2 lignes.
Je comprends qu'Oracle effectue la 2nde jointure externe sans tenir compte du résultat de la 3e... mais comment faire autrement?

Reply

Marsh Posté le 16-09-2008 à 17:26:35   

Reply

Marsh Posté le 16-09-2008 à 17:29:10    

pourquoi tu fais une jointure ouverte ? (parceque pour le résultat que tu veux, tu n'en a pas besoin)
 
et si tu as une version >= 9i, alors écrit ta requête avec la syntaxe SQL92, à savoir en utilisant les jointures de type "right outer join", qui est à la fois plus explicite pour toi et pour oracle. si je comprends bien éventuellement ton problème, je pense d'ailleurs que la réponse se trouve dans cette syntaxe. la jointure entre b et c est un inner join et non un outer join

Message cité 1 fois
Message édité par MagicBuzz le 16-09-2008 à 17:30:04
Reply

Marsh Posté le 16-09-2008 à 19:08:42    

MagicBuzz a écrit :

pourquoi tu fais une jointure ouverte ? (parceque pour le résultat que tu veux, tu n'en a pas besoin)
 
et si tu as une version >= 9i, alors écrit ta requête avec la syntaxe SQL92, à savoir en utilisant les jointures de type "right outer join", qui est à la fois plus explicite pour toi et pour oracle. si je comprends bien éventuellement ton problème, je pense d'ailleurs que la réponse se trouve dans cette syntaxe. la jointure entre b et c est un inner join et non un outer join


 
Merci de ta réponse MagicBuzz.
Je travaille en 10g. J'y ai pensé, et tenté de remplacer la jointure externe par une jointure interne entre B et C (AND b.NUM = c.NUM), mais à ce moment je n'ai plus aucun résultat! Et cela équivaut, sauf erreur de ma part, à un inner join non? Je ne suis pas très familiarisé avec l'écriture ANSI mais je vais approfondir en tout cas. Merci de la suggestion.

Reply

Marsh Posté le 17-09-2008 à 00:42:57    

ben tout dépend. déjà, est-ce que tu as forcément des tupes dans chacune des trois tables, ou si lorsqu'une valeur n'est présente que dans A, tu peux quand même l'afficher en disant qu'il n'y a rien dans C ?

Reply

Marsh Posté le 17-09-2008 à 00:55:13    

Exemple de ce que j'ai compris à 1h du matin :wahoo:
 
Sous SQL Server 2008. Mise à part les "go", les types et les [] d'échappement, la syntaxe devrait être rigoureusement la même.

Code :
  1. CREATE TABLE a (id numeric PRIMARY KEY, label varchar(30) NOT NULL)
  2. go
  3. CREATE TABLE b (id numeric NOT NULL, [num] numeric NOT NULL)
  4. go
  5. CREATE TABLE c ([num] numeric PRIMARY KEY, val numeric NOT NULL)
  6. go
  7. ALTER TABLE b ADD PRIMARY KEY (id, [num])
  8. go
  9. ALTER TABLE b ADD FOREIGN KEY (id) REFERENCES a (id)
  10. go
  11. -- Clé étrangère désactivées car les données de B font référence à des données qui n'existent pas dans C
  12. -- alter table b add foreign key ([num]) references c ([num])
  13. --go
  14. INSERT INTO a (id, label) VALUES (1, 'Ligne 1');
  15. INSERT INTO a (id, label) VALUES (2, 'Ligne 2');
  16. INSERT INTO a (id, label) VALUES (3, 'Ligne 3');
  17. INSERT INTO c ([num], val) VALUES (4, 7);
  18. INSERT INTO c ([num], val) VALUES (5, 8);
  19. INSERT INTO b (id, [num]) VALUES (1, 4);
  20. INSERT INTO b (id, [num]) VALUES (2, 5);
  21. INSERT INTO b (id, [num]) VALUES (2, 6);
  22. INSERT INTO b (id, [num]) VALUES (3, 7);
  23.  
  24. SELECT a.id, c.val
  25. FROM a INNER JOIN b ON b.id = a.id
  26. INNER JOIN c ON c.num = b.num;
  27.  
  28. SELECT a.id, c.val
  29. FROM a INNER JOIN b ON b.id = a.id
  30. LEFT OUTER JOIN c ON c.num = b.num;
  31.  
  32. SELECT a.id, c.val
  33. FROM a INNER JOIN b ON b.id = a.id
  34. INNER JOIN c ON c.num = b.num
  35. WHERE a.id = 2 AND c.val = 8;


 
Sortie :


 
(1 ligne(s) affectée(s))
 
(1 ligne(s) affectée(s))
 
(1 ligne(s) affectée(s))
 
(1 ligne(s) affectée(s))
 
(1 ligne(s) affectée(s))
 
(1 ligne(s) affectée(s))
 
(1 ligne(s) affectée(s))
 
(1 ligne(s) affectée(s))
 
(1 ligne(s) affectée(s))
id                                      val
--------------------------------------- ---------------------------------------
1                                       7
2                                       8
 
(2 ligne(s) affectée(s))
 
id                                      val
--------------------------------------- ---------------------------------------
1                                       7
2                                       8
2                                       NULL
3                                       NULL
 
(4 ligne(s) affectée(s))
 
id                                      val
--------------------------------------- ---------------------------------------
2                                       8
 
(1 ligne(s) affectée(s))


Message édité par MagicBuzz le 17-09-2008 à 01:08:18
Reply

Marsh Posté le 17-09-2008 à 01:04:00    

Donc pour moi c'est bêtement du inner join.
 
Si par contre, dans le cas où il n'y a pas d'entrée val = 8 pour id = 2, tu n'auras pas de ligne. Si tu veux une ligne avec id = 2 et val = null en sortie, alors c'est plus sioux :
 

Code :
  1. SELECT a.id, (SELECT MIN(c.val) FROM c INNER JOIN b ON b.num = c.num WHERE b.id = a.id AND c.val = 8) AS val
  2. FROM a
  3. WHERE a.id = 2;


(Ca doit pouvoir s'écrire autrement)
 


id                                      val
--------------------------------------- ---------------------------------------
2                                       8
 
(1 ligne(s) affectée(s))
 
id                                      val
--------------------------------------- ---------------------------------------
2                                       NULL
 
(1 ligne(s) affectée(s))


(premier coup avec la ligne existante dans C et second coup avec la ligne non existante dans C)
 
Le "MIN" c'est pour s'assurer qu'en cas de doublons dans VAL (deux lignes avec val = 8 pour des num différents rattachés au même id) la requête ne plante pas. Tu n'auras par contre qu'une seule ligne dans ce cas. Si tu veux avoir deux lignes, il faut déplacer la jointure de la sous-requête à la requête principale :
 

Code :
  1. SELECT a.id, (SELECT MIN(c.val) FROM c WHERE c.num = b.num AND c.val = 9) AS val
  2. FROM a
  3. INNER JOIN b ON b.id = a.id
  4. WHERE a.id = 2;


 


id                                      val
--------------------------------------- ---------------------------------------
2                                       NULL
 
(1 ligne(s) affectée(s))


Message édité par MagicBuzz le 17-09-2008 à 01:07:17
Reply

Marsh Posté le 17-09-2008 à 15:46:18    

Ok, j'ai peut-être trop simplifié mon problème, je vais essayer d'être plus clair.
Voici ma requête réécrite, version ANSI, selon tes conseils:
 


SELECT a1.id, c1.val, c2.val,
FROM A a1 INNER JOIN B b1 ON b1.id = a1.id
INNER JOIN C c1 ON c1.id = b1.num,
A a2 INNER JOIN B b2 ON b2.id = a2.id
INNER JOIN C c2 ON c2.id = b2.num
WHERE a1.id = 2
AND a2.id = 2
AND c1.num = 8
AND c2.num = 9


 
Explication:
Chaque ligne de A contient (ou non) une entrée dans la table B.
Chaque ligne de la table B pointe sur la table C
Certaines lignes de la table C sont du type qui m'intéresse (8 et 9, soit deux types, d'où la nécessité selon moi d'avoir a1 ET a2, c1 ET c2).
 
Je veux, pour l'entrée de A ayant id=2, une seule ligne en résultat, et qui contient:


ID    NUM    NUM
2     4        5


 
Mais avec la requête ci-dessus, je ne récupère aucun résultat...

Reply

Marsh Posté le 17-09-2008 à 16:20:56    

Entre C1 et C2, la seule chose qui change c'est le contenu de la colonne "num", n'est ce pas, et tu n'as pas besoin qu'une ligne de A corresponde aux deux types de C recherché.
 
Dans ce cas pourquoi ne pas simplifier en ne gardant qu'une seule instance de la table C et en utilisant un "OR"?
 
Ca donnerait quelque chose du genre :

Citation :

SELECT A.id, C.val
FROM A INNER JOIN B ON B.id = A.id
INNER JOIN C ON C.id = B.num
WHERE a1.id = 2
AND (c1.num = 8
OR c2.num = 9)

Reply

Marsh Posté le 17-09-2008 à 16:59:10    

Salut Omega2 et merci.
Je suppose que la requête que tu proposais était:

Code :
  1. SELECT A.id, C.val
  2. FROM A INNER JOIN B ON B.id = A.id
  3. INNER JOIN C ON C.id = B.num
  4. WHERE a1.id = 2
  5. AND (c1.num = 8
  6. OR c1.num = 9)


 
laquelle me ramène deux lignes:


id  val
2   4
2   5


mais je ne veux qu'une seule ligne par id:


id  val  val
2   4    5


Je continue à chercher merci de vos lumières!
 
 

omega2 a écrit :

Entre C1 et C2, la seule chose qui change c'est le contenu de la colonne "num", n'est ce pas, et tu n'as pas besoin qu'une ligne de A corresponde aux deux types de C recherché.
 
Dans ce cas pourquoi ne pas simplifier en ne gardant qu'une seule instance de la table C et en utilisant un "OR"?
 
Ca donnerait quelque chose du genre :

Citation :

SELECT A.id, C.val
FROM A INNER JOIN B ON B.id = A.id
INNER JOIN C ON C.id = B.num
WHERE a1.id = 2
AND (c1.num = 8
OR c2.num = 9)



Message édité par Ju - le 17-09-2008 à 17:01:26
Reply

Marsh Posté le 17-09-2008 à 17:14:22    

oups, oui : c1
 
Regarde la syntaxe pour faire des requêtes imbriqués telles que dans le dernier exemple de MagicBuzz. En utilisant cette méthode et en faisant une requête par "cX.num" tu peux obtenir le résultat que tu veux.

Reply

Marsh Posté le 17-09-2008 à 17:14:22   

Reply

Marsh Posté le 18-09-2008 à 13:48:24    

si tu as un nombre indéterminé de val que tu souhaites avoir sur la même ligne -> PL/SQL

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed