332 votes

Oracle SQL : Mettre à jour une table avec les données d'une autre table

Tableau 1 :

id    name    desc
-----------------------
1     a       abc
2     b       def
3     c       adf

Tableau 2 :

id    name    desc
-----------------------
1     x       123
2     y       345

Dans oracle SQL, comment puis-je exécuter un mise à jour sql une requête qui peut mettre à jour la table 1 avec la table 2 name y desc en utilisant le même id ? Le résultat final que j'obtiendrais serait donc

Tableau 1 :

id    name    desc
-----------------------
1     x       123
2     y       345
3     c       adf

La question est tirée de mettre à jour une table avec les données d'une autre mais spécifiquement pour oracle SQL.

2 votes

0 votes

Vous devez revenir à votre autre question, annuler cette réponse et préciser que vous avez besoin de la syntaxe Oracle PLSQL.

3 votes

@p.campbell, Ce n'est pas ma question...

643voto

Justin Cave Points 114578

C'est ce qu'on appelle une mise à jour corrélée

UPDATE table1 t1
   SET (name, desc) = (SELECT t2.name, t2.desc
                         FROM table2 t2
                        WHERE t1.id = t2.id)
 WHERE EXISTS (
    SELECT 1
      FROM table2 t2
     WHERE t1.id = t2.id )

En supposant que la jointure aboutisse à une vue conservée par clé, vous pouvez également

UPDATE (SELECT t1.id, 
               t1.name name1,
               t1.desc desc1,
               t2.name name2,
               t2.desc desc2
          FROM table1 t1,
               table2 t2
         WHERE t1.id = t2.id)
   SET name1 = name2,
       desc1 = desc2

9 votes

Dans votre premier exemple de code : La clause WHERE externe est-elle nécessaire pour obtenir des résultats corrects ? Ou l'utilisez-vous uniquement pour accélérer la requête ?

65 votes

@totoro - Dans le premier exemple, les WHERE EXISTS vous empêche de mettre à jour une ligne dans t1 s'il n'y a pas de ligne correspondante dans t2 . Sans elle, chaque ligne de t1 sera mis à jour et les valeurs seront définies comme suit NULL s'il n'y a pas de ligne correspondante dans t2 . Ce n'est généralement pas ce que l'on souhaite. WHERE EXISTS est généralement nécessaire.

5 votes

Il convient d'ajouter que le SELECT ... FROM t2 doit donnent lieu à une ligne unique. Cela signifie que vous devez effectuer une sélection sur tous les champs qui constituent une clé unique -- une clé primaire non unique n'est pas suffisante. Sans l'unicité, vous êtes réduit à quelque chose comme La boucle de @PaulKarr -- et s'il n'y a pas de corrélation unique, plusieurs lignes cibles peuvent être mises à jour pour chaque ligne source.

27voto

Yahia Points 49011

Essayez

UPDATE Table1 T1 SET
T1.name = (SELECT T2.name FROM Table2 T2 WHERE T2.id = T1.id),
T1.desc = (SELECT T2.desc FROM Table2 T2 WHERE T2.id = T1.id)
WHERE T1.id IN (SELECT T2.id FROM Table2 T2 WHERE T2.id = T1.id);

9 votes

L'inconvénient de cette méthode est que l'instruction SELECT est répétée 3 fois. Dans les exemples complexes, cela peut s'avérer rédhibitoire.

15voto

Pau Karr Points 46
Update table set column = (select...)

n'a jamais fonctionné pour moi puisque l'ensemble n'attend qu'une seule valeur - Erreur SQL : ORA-01427 : une sous-requête à une ligne renvoie plus d'une ligne.

Voici la solution :

BEGIN
For i in (select id, name, desc from table1) 
LOOP
Update table2 set name = i.name, desc = i.desc where id = i.id;
END LOOP;
END;

C'est exactement comme ça que vous l'exécutez sur la feuille de travail SQLDeveloper. Ils disent que c'est lent mais c'est la seule solution qui a fonctionné pour moi dans ce cas.

0 votes

Quelqu'un peut-il expliquer pourquoi cela mérite un -2 à la réputation ? LOL.

14 votes

Je ne l'ai pas descendu, mais ce n'est pas une bonne solution. Premièrement, si la sous-sélection renvoie plusieurs valeurs, alors la boucle for écrasera le nom de la table 2 plusieurs fois pour certains ou tous les enregistrements (ce qui n'est pas propre). Deuxièmement : il n'y a pas de clause order by, donc cela se produira de manière imprévisible (c'est-à-dire que la dernière valeur des données non ordonnées l'emporte). Troisièmement : il sera beaucoup plus lent. En supposant que le résultat de la boucle for était prévu, le subselect original aurait pu être réécrit d'une manière contrôlée pour retourner seulement 1 valeur pour chaque enregistrement... la manière la plus simple serait (select min(name)...)

0 votes

C'était exactement ce dont j'avais besoin. Merci (+1)

10voto

ant Points 65

Il semble qu'il y ait une réponse encore meilleure avec la clause "in" qui permet de des clés multiples pour la jointure :

update fp_active set STATE='E', 
   LAST_DATE_MAJ = sysdate where (client,code) in (select (client,code) from fp_detail
  where valid = 1) ...

L'exemple complet est ici : http://forums.devshed.com/oracle-development-96/how-to-update-from-two-tables-195893.html - à partir des archives web car le lien était mort.

Le problème est d'avoir les colonnes que vous voulez utiliser comme clé entre parenthèses dans la clause where avant 'in' et d'avoir l'instruction select avec les mêmes noms de colonnes entre parenthèses. where ( colonne1, colonne2 ) dans ( sélectionnez ( colonne1, colonne2 ) de la table où "l'ensemble que je veux". );

0 votes

Le lien a expiré. ( 404 )

-2voto

Jim P Points 209

Si votre table t1 et sa sauvegarde t2 ont beaucoup de colonnes, voici une façon compacte de le faire.

De plus, mon problème connexe était que seules certaines des colonnes étaient modifiées et que de nombreuses lignes n'avaient pas été modifiées dans ces colonnes. Je voulais donc les laisser tranquilles - en fait, restaurer un sous-ensemble de colonnes à partir d'une sauvegarde de la table entière. Si vous souhaitez simplement restaurer toutes les lignes, ignorez la clause where.

Bien sûr, le moyen le plus simple serait de supprimer et d'insérer en tant que sélection, mais dans mon cas, j'avais besoin d'une solution avec seulement des mises à jour.

Le truc, c'est que lorsque vous faites un select * à partir d'une paire de tables avec des noms de colonnes en double, la deuxième sera nommée _1. Voici donc ce que j'ai trouvé :

  update (
    select * from t1 join t2 on t2.id = t1.id
    where id in (
      select id from (
        select id, col1, col2, ... from t2
        minus select id, col1, col2, ... from t1
      )
    )
  ) set col1=col1_1, col2=col2_1, ...

0 votes

Cela ne fonctionne pas pour moi dans Oracle 11g. Pouvez-vous créer un exemple fonctionnel de cette méthode ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X