UPDATE avec jointure

UPDATE avec jointure - SQL/NoSQL - Programmation

Marsh Posté le 29-12-2009 à 16:55:45    

Bonjour tout le monde,
 
Je suis sous Oracle 10g et j'ai besoin de faire une requête qui me paraît toute simple :  
 
UPDATE TABLE1 T1
SET T1.Champ1 =  
SELECT T2.Champ1
FROM TABLE2 T2
INNER JOIN TABLE1 T1 ON T1.Champ2 = T2.Champ2
 
J'ai beau tourner dans tous les sens, ça me sort toujours des erreurs, pourtant je suis presque sur d'avoir déjà utilisé cette syntaxe, peut-être sur un autre SGBD...
 
Si quelqu'un a une idée...
 
Merci,

Reply

Marsh Posté le 29-12-2009 à 16:55:45   

Reply

Marsh Posté le 29-12-2009 à 17:08:52    

Essaye plutot :

Code :
  1. UPDATE TABLE1 T1
  2. SET T1.Champ1 =  (
  3. SELECT T2.Champ1
  4. FROM TABLE2 T2
  5. WHERE T1.Champ2 = T2.Champ2 )

Reply

Marsh Posté le 29-12-2009 à 17:13:20    

Yop, j'y ai cru (ça a tourné quelques secondes...) et puis la fameuse erreur 1427 : "single-row subquery returns more than one row"...
 
Je comprends bien ce que ça veut dire, mais ça m'arrangerait de pouvoir mettre à jour toute ma table à partir des champs d'une autre table sur une jointure entre les deux...

Reply

Marsh Posté le 29-12-2009 à 17:16:22    

Bon ben je suis tombé la dessus :  
http://forum.hardware.fr/hfr/Progr [...] 5595_1.htm
(ça date de 2002, mon seul espoir est que ça ait été implémenté depuis, mais je n'y crois pas trop)...

Reply

Marsh Posté le 29-12-2009 à 17:18:37    

lol, ben ca veux dire que ton sous select renvoie pls lignes, comment veux-tu que oracle s'y retrouve.
Ta jointure doit se faire sur des clefs sinon c'est mort, c'est comme si tu lui demande de mettre a jour les prenoms de tous les gars dont le nom est martin juste avec leur nom.
Si toutes les valeurs sont les memes, essaye :

Code :
  1. UPDATE TABLE1 T1
  2. SET T1.Champ1 =  (
  3. SELECT MAX(T2.Champ1)
  4. FROM TABLE2 T2
  5. WHERE T1.Champ2 = T2.Champ2 )


mais a prendre avec des pincettes, tu va fusiller ta base si les valeurs sont differentes et qu'elles doivent le rester.
mais ce que tu veux probablement ecrire est

Code :
  1. UPDATE TABLE1 T1
  2. SET T1.Champ1 =  (
  3. SELECT T2.Champ1
  4. FROM TABLE2 T2
  5. WHERE T1.clef = T2.clef )

Reply

Marsh Posté le 29-12-2009 à 17:26:52    

Non, ce que je veux ecrire est exactement ce qui se "comprend" dans ma requête initiale, à savoir que je veux mettre à jour un champ de la table 1 à partir d'une valeur de la table 2, en ayant une clé commune entre les deux tables...
 
Par exemple, j'ai un référentiel de pays avec leur code ISO, et une table adresse avec uniquement le code ISO, et je souhaite mettre à jour le champ "libelle_pays" à partir de ma table de référence :  
 
UPDATE ADRESSE AD
SET AD.Libelle_pays =  
(SELECT REF.Libelle_pays
FROM REFERENTIEL_PAYS REF
INNER JOIN ADRESSE AD ON AD.Code_Pays = REF.Code_Pays)
 
Mon SELECT renvoie bien plusieurs lignes (autant qu'il y en a dans ma table ADRESSE), mais l'UPDATE se fait bien sur une seule ligne à la fois, même si il en fait plusieurs dans le même ensemble...
 
J'ai trouvé ça :  
 
http://geekswithblogs.net/WillSmit [...] again.aspx  
apparemment ça passe bien sous MS-SQL, et quand j'adapte avec la solution proposée, je me retrouve avec une "insuficient privilege", je continue de creuser...


Message édité par Tibar le 29-12-2009 à 17:27:34
Reply

Marsh Posté le 29-12-2009 à 18:23:14    

Bon alors apparemment, il existerait une syntaxe :  
 
MERGE INTO ADRESSE AD
USING (
      SELECT AD.Code_Pays cp_ad, RE.Libelle_Pays lp_re, RE.Code_Pays cp_re
      FROM ADRESSE AD, REFERENTIEL_PAYS RE
      WHERE AD.Code_Pays = RE.Code_Pays
      )  
      DRV
ON (AD.Code_Pays = DRV.cp_re)
WHEN MATCHED THEN UPDATE SET AD.Libelle_Pays = DRV.lp_re
 
mais quand j'essaie sur ma table (qui ne concerne pas des pays ni des libellés de pays), j'obtiens une erreur 30926 "unable to get a stable set of rows in the source table"...
 
La suite au prochain épisode, je pars sur un CREATE AS SELECT, au moins je sais faire une jointure, on verra pour rapatrier les données...

Reply

Marsh Posté le 29-12-2009 à 23:23:03    

fred777888999 a écrit :

lol, ben ca veux dire que ton sous select renvoie pls lignes, comment veux-tu que oracle s'y retrouve.
Ta jointure doit se faire sur des clefs sinon c'est mort, c'est comme si tu lui demande de mettre a jour les prenoms de tous les gars dont le nom est martin juste avec leur nom.
Si toutes les valeurs sont les memes, essaye :

Code :
  1. UPDATE TABLE1 T1
  2. SET T1.Champ1 =  (
  3. SELECT MAX(T2.Champ1)
  4. FROM TABLE2 T2
  5. WHERE T1.Champ2 = T2.Champ2 )


mais a prendre avec des pincettes, tu va fusiller ta base si les valeurs sont differentes et qu'elles doivent le rester.
mais ce que tu veux probablement ecrire est

Code :
  1. UPDATE TABLE1 T1
  2. SET T1.Champ1 =  (
  3. SELECT T2.Champ1
  4. FROM TABLE2 T2
  5. WHERE T1.clef = T2.clef )



 
En fait, je ne comprends pas pourquoi il faudrait absolument que les données de jointure dans T1 et T2 soient des clés. Apparemment Oracle estime que les données ne peuvent pas être "fiables" si la jointure ne se fait pas sur une clé... Dommage pour moi...

Reply

Marsh Posté le 30-12-2009 à 09:03:57    

le merge ne t'aidera pas.
 
ta requete doit avoir la gueule:

Code :
  1. UPDATE ADRESSE AD
  2. SET AD.Libelle_pays =  
  3. (SELECT REF.Libelle_pays
  4. FROM REFERENTIEL_PAYS REF
  5. WHERE AD.Code_Pays = REF.Code_Pays)


 
et si tu as 1427 : "single-row subquery returns more than one row", c'est que tu as plusieurs lignes pour un même Code_Pays dans ta table REFERENTIEL_PAYS, et donc a moins de mettre un distinct devant REF.Libelle_pays tu vas devoir établir une règle pour lui dire quelle ligne prendre
 

Reply

Marsh Posté le 30-12-2009 à 09:19:42    

fred777888999 a écrit :

lol, ben ca veux dire que ton sous select renvoie pls lignes, comment veux-tu que oracle s'y retrouve.
Ta jointure doit se faire sur des clefs sinon c'est mort, c'est comme si tu lui demande de mettre a jour les prenoms de tous les gars dont le nom est martin juste avec leur nom.
Si toutes les valeurs sont les memes, essaye :

Code :
  1. UPDATE TABLE1 T1
  2. SET T1.Champ1 =  (
  3. SELECT MAX(T2.Champ1)
  4. FROM TABLE2 T2
  5. WHERE T1.Champ2 = T2.Champ2 )


mais a prendre avec des pincettes, tu va fusiller ta base si les valeurs sont differentes et qu'elles doivent le rester.
mais ce que tu veux probablement ecrire est

Code :
  1. UPDATE TABLE1 T1
  2. SET T1.Champ1 =  (
  3. SELECT T2.Champ1
  4. FROM TABLE2 T2
  5. WHERE T1.clef = T2.clef )



 
 
Plutôt que MAX, j'utiliserais "DISTINCT", histoire d'être sûr de planter quand même s'il y a des lignes différentes :)

Reply

Marsh Posté le 30-12-2009 à 09:19:42   

Reply

Marsh Posté le 30-12-2009 à 09:54:58    

De toutes facons, tu peux mettre ce que tu veux, le probleme est celui que casimir a exprime mieux que moi. Si tu as plusieurs pays avec le meme code, comment veux-tu qu'oracle sache quel libelle mettre ?
Ca peut s'identifier avec

Code :
  1. SELECT COUNT(*), REF.Code_Pays , MAX(REF.Libelle_pays)
  2. FROM REFERENTIEL_PAYS REF
  3. GROUP BY REF.Code_Pays
  4. ORDER BY 1 DESC


et une fois ta table corrigée tu peux utiliser merge ou update c'est sans importance.

Reply

Marsh Posté le 30-12-2009 à 10:16:43    

Salut tout le monde,
 
En effet c'était bien un problème de doublons dans la table référentiel (cas évidemment impossible d'après les règles de gestion)... Ça m'apprendra à faire confiance à des documents plutôt que de contrôler...
 
Fred, dans ta requête pour détecter les doublons, le MAX(REF.Libelle_Pays) est juste là à titre indicatif ?
 
Enfin, 1j pour faire un update alors que la réponse était dans la question, belle performance de ma part...
 
Merci à tous,

Reply

Marsh Posté le 30-12-2009 à 10:50:30    

Oui, c'est juste pour avoir un des libelles au hasard, mais c'est normalement sur le code que tu dois travailler.

Reply

Sujets relatifs:

Leave a Replay

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