Instruction de mise à jour avec jointure interne sur Oracle

298

J'ai une requête qui fonctionne bien dans MySQL, mais lorsque je l'exécute sur Oracle, j'obtiens l'erreur suivante:

Erreur SQL: ORA-00933: commande SQL non terminée correctement
00933. 00000 - "Commande SQL non terminée correctement"

La requête est:

UPDATE table1
INNER JOIN table2 ON table1.value = table2.DESC
SET table1.value = table2.CODE
WHERE table1.UPDATETYPE='blah';
user169743
la source
Lorsque j'ai essayé de configurer table2 dans Oracle pour tester ma réponse, j'ai constaté qu'Oracle rejetait DESC comme nom de colonne.
Janek Bogucki
Désolé, je viens d'abréger le nom de la colonne d'origine pour descendre, ce n'est évidemment pas cela dans la base de données
user169743

Réponses:

412

Cette syntaxe n'est pas valide dans Oracle. Tu peux le faire:

UPDATE table1 SET table1.value = (SELECT table2.CODE
                                  FROM table2 
                                  WHERE table1.value = table2.DESC)
WHERE table1.UPDATETYPE='blah'
AND EXISTS (SELECT table2.CODE
            FROM table2 
            WHERE table1.value = table2.DESC);

Ou vous pourriez être en mesure de le faire:

UPDATE 
(SELECT table1.value as OLD, table2.CODE as NEW
 FROM table1
 INNER JOIN table2
 ON table1.value = table2.DESC
 WHERE table1.UPDATETYPE='blah'
) t
SET t.OLD = t.NEW

Cela dépend si la vue en ligne est considérée comme pouvant être mise à jour par Oracle (La mise à jour pour la deuxième instruction dépend de certaines règles répertoriées ici ).

Tony Andrews
la source
5
J'ai fait le deuxième exemple, mais j'ai dû ajouter des alias aux noms de colonnes dans la sélection, puis les référencer par leurs noms dans le SET, mais cela a fonctionné, merci
Gustavo Rubio
41
Le deuxième exemple a l'avantage de vous permettre de tester le SQL avant d'effectuer réellement la mise à jour.
Daniel Reis
10
Le deuxième exemple a fonctionné pour moi. J'aime celui-là car il a l'air propre et lisible. Je ne sais pas quels sont les avantages et les inconvénients entre les deux en matière de performances. Mais je n'étais pas inquiet à ce sujet pour le moment car j'ai utilisé ceci pour un script unique pour corriger les mauvaises données.
nemo
5
Deuxième travaillé pour moi :). Oracle est un animal fort mais bizarre: /
elrado
10
Explication sur l'exigence de clé préservée pour les jointures pouvant être mises à jour: asktom.oracle.com/pls/asktom/…
Vadzim
202

Utilisez ceci:

MERGE
INTO    table1 trg
USING   (
        SELECT  t1.rowid AS rid, t2.code
        FROM    table1 t1
        JOIN    table2 t2
        ON      table1.value = table2.DESC
        WHERE   table1.UPDATETYPE='blah'
        ) src
ON      (trg.rowid = src.rid)
WHEN MATCHED THEN UPDATE
    SET trg.value = code;
Quassnoi
la source
2
Fonctionne parfaitement, mais Oracle m'a demandé de dire merge into table 1 tet ainsi de suite.
Michael-O
1
Tard dans la fête, mais c'est toujours un bon fil. J'ai besoin de savoir, quoique ... ai-je raté quelque chose? Table principale, "table1". Dans USING, table1 aliasé t1. Table2, aliasée t2, mais dans ON, les références sont ...? Table1 externe - pas t1 - est-ce une référence à la table externe ou un type? Tableau 2? Pas t2? Je suis confus. Fan de meilleurs alias ...
Marc
Juste un point ici, si votre clé (trg.rowid ou src.rid) a un élément dupliqué, cette clause renvoie
Henrique
@Marc Dans le ON, trgest l'alias de la table principale (table table1"externe" par votre logique) et fait srcréférence au USINGgroupe ("table interne" par votre logique). Mais oui, j'aurais probablement pu être mieux référencé, mais j'ai pu le suivre.
vapcguy
1
@supernova: la réponse de tony met à jour une vue en ligne. Cela peut fonctionner dans certains cas, mais la vue doit être «préservée par clé» (chaque table jointe doit être jointe par égalité sur sa clé primaire ou par ailleurs un ensemble de champs unique). Cela garantit que chaque enregistrement de la table cible contribue à au plus un enregistrement dans l'ensemble de lignes résultant et, par conséquent, chaque enregistrement de la table cible est mis à jour au plus une fois.
Quassnoi
25

MERGEavec WHEREclause:

MERGE into table1
USING table2
ON (table1.id = table2.id)
WHEN MATCHED THEN UPDATE SET table1.startdate = table2.start_date
WHERE table1.startdate > table2.start_date;

Vous avez besoin de la WHEREclause car les colonnes référencées dans la ONclause ne peuvent pas être mises à jour.

Roland
la source
Cette version est sans doute plus propre, mais elle n'est pas compatible avec les déclencheurs, car je ne connais aucun moyen d'éviter de déclencher des déclencheurs de mise à jour pour les lignes inchangées en utilisant cette syntaxe. (Je suppose que les déclencheurs sont nécessaires pour les lignes modifiées .)
sf_jeff
14
 UPDATE ( SELECT t1.value, t2.CODE
          FROM table1 t1
          INNER JOIN table2 t2 ON t1.Value = t2.DESC
          WHERE t1.UPDATETYPE='blah')
 SET t1.Value= t2.CODE
Morten Anderson
la source
11

N'utilisez pas certaines des réponses ci-dessus.

Certains suggèrent l'utilisation de SELECT imbriqué, ne le faites pas, c'est extrêmement lent. Si vous avez beaucoup d'enregistrements à mettre à jour, utilisez join, donc quelque chose comme:

update (select bonus 
        from employee_bonus b 
        inner join employees e on b.employee_id = e.employee_id 
        where e.bonus_eligible = 'N') t
set t.bonus = 0;

Voir ce lien pour plus de détails. http://geekswithblogs.net/WillSmith/archive/2008/06/18/oracle-update-with-join-again.aspx .

Assurez-vous également qu'il existe des clés primaires sur toutes les tables que vous joignez.

duvo
la source
7

Comme indiqué ici , la syntaxe générale de la première solution proposée par Tony Andrews est:

update some_table s
set   (s.col1, s.col2) = (select x.col1, x.col2
                          from   other_table x
                          where  x.key_value = s.key_value
                         )
where exists             (select 1
                          from   other_table x
                          where  x.key_value = s.key_value
                         )

Je pense que c'est intéressant surtout si vous voulez mettre à jour plus d'un champ.

Alexis Dufrenoy
la source
Ça ne marche pas pour moi. Il met à jour la table entière.
Natassia Tavares
3

Cette syntaxe suivante fonctionne pour moi.

UPDATE
(SELECT A.utl_id,
    b.utl1_id
    FROM trb_pi_joint A
    JOIN trb_tpr B
    ON A.tp_id=B.tp_id Where A.pij_type=2 and a.utl_id is null
)
SET utl_id=utl1_id;
Hemant
la source
@JimGarrison Veuillez rééditer cette réponse afin que je puisse supprimer mon downvote .... J'essayais d'utiliser cette syntaxe et cela ne mettait pas à jour ma table. J'ai découvert pourquoi - mon SETfaisait un REPLACEet j'essayais de vider une chaîne particulière dans la colonne - se révèle Oracle traite ''comme nul, et ce champ n'a pas pu être annulé. Je pensais que la syntaxe mettait simplement à jour une table temporaire au lieu de la vraie, mais je me trompais.
vapcguy
2

En utilisant description au lieu de desc pour table2,

update
  table1
set
  value = (select code from table2 where description = table1.value)
where
  exists (select 1 from table2 where description = table1.value)
  and
  table1.updatetype = 'blah'
;
Janek Bogucki
la source
pourquoi vous voulez lancer deux requêtes distinctes sur table2
Jitendra Vispute
2

Cela fonctionne bien oracle

merge into table1 t1
using (select * from table2) t2
on (t1.empid = t2.empid)
when matched then update set t1.salary = t2.salary
user5299305
la source
Peut définir plusieurs propriétés en ajoutant une virgule à la fin de cela. Je devais faire t1.First_Name = t2.FirstName, t1.Last_Name = t2.LastNamesur une table après l'avoir fait correspondre dans la colonne "UserName" ( t1.UserName = t2.UserName) pour récupérer leur nom dans une table appelée UserInfo ( select * from UserInfo) t2). La base de données était telle qu'elle utilisait UserName comme clé primaire pour UserInfo partout, au lieu de placer FirstName et LastName dans la table, directement. Cela a corrigé cela!
vapcguy
Cette réponse n'ajoute rien à la réponse déjà fournie par Quassnoi cinq ans avant la vôtre.
Forage
0
UPDATE table1 t1
SET t1.value = 
    (select t2.CODE from table2 t2 
     where t1.value = t2.DESC) 
WHERE t1.UPDATETYPE='blah';
afnhsn
la source
0
UPDATE IP_ADMISSION_REQUEST ip1
SET IP1.WRIST_BAND_PRINT_STATUS=0
WHERE IP1.IP_ADM_REQ_ID        =
  (SELECT IP.IP_ADM_REQ_ID
  FROM IP_ADMISSION_REQUEST ip
  INNER JOIN VISIT v
  ON ip.ip_visit_id=v.visit_id
  AND v.pat_id     =3702
  ); `enter code here`
user5685973
la source
0

Par souci d'exhaustivité, et parce que nous parlons d'Oracle, cela pourrait également le faire:

declare
begin
  for sel in (
    select table2.code, table2.desc
    from table1
    join table2 on table1.value = table2.desc
    where table1.updatetype = 'blah'
  ) loop
    update table1 
    set table1.value = sel.code
    where table1.updatetype = 'blah' and table1.value = sel.desc;    
  end loop;
end;
/
Edu Castrillon
la source
1
Cela pourrait le faire, mais il s'agit de la manière la plus lente possible.
APC
-1
UPDATE (SELECT T.FIELD A, S.FIELD B
FROM TABLE_T T INNER JOIN TABLE_S S
ON T.ID = S.ID)
SET B = A;

A et B sont des champs d'alias, vous n'avez pas besoin de pointer la table.

Dan Anderson
la source
1
Salut Dan. Vous postez sur une assez vieille question qui a déjà de très bonnes réponses. Pouvez-vous expliquer quand votre question est préférable aux autres solutions?
Noel Widmer
1
Bien sûr, j'ai vu une réponse où b = a a été écrit en pointant le nom de la table (table1.B = table2.A) mais il n'est pas nécessaire de pointer la table.
Dan Anderson
Vous mettez actuellement à jour les champs de la vue, qui sont mappés à la table. Si la vue intérieure était aliasée h, la version "auto-documentée" serait "set hb = ha".
sf_jeff
-4
update table1  a 
   set a.col1='Y' 
 where exists(select 1 
                from table2 b
               where a.col1=b.col1 
                 and a.col2=b.col2
             )
tester
la source