Declaración de actualización con unión interna en Oracle

298

Tengo una consulta que funciona bien en MySQL, pero cuando la ejecuto en Oracle obtengo el siguiente error:

Error de SQL: ORA-00933: el comando SQL no finalizó correctamente
00933. 00000 - "El comando SQL no finalizó correctamente"

La consulta es:

UPDATE table1
INNER JOIN table2 ON table1.value = table2.DESC
SET table1.value = table2.CODE
WHERE table1.UPDATETYPE='blah';
usuario169743
fuente
Cuando intenté configurar la tabla2 en Oracle para probar mi respuesta, descubrí que Oracle rechazaba DESC como nombre de columna.
Janek Bogucki
Lo siento, simplemente abreviado el nombre original de la columna a la descripción de su obviamente que en el PP
user169743

Respuestas:

412

Esa sintaxis no es válida en Oracle. Puedes hacerlo:

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);

O tal vez puedas hacer esto:

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

Depende de si Oracle considera que la vista en línea se puede actualizar ( ser actualizable para la segunda declaración depende de algunas reglas enumeradas aquí ).

Tony Andrews
fuente
55
Hice el segundo ejemplo, pero tuve que agregar alias a los nombres de columna en la selección y luego hacer referencia a ellos por sus nombres en el SET, pero funcionó, gracias
Gustavo Rubio
41
El segundo ejemplo tiene la ventaja de permitirle probar el SQL antes de realizar la actualización.
Daniel Reis
10
El segundo ejemplo funcionó para mí. Me gusta porque se ve limpio y legible. No sé cuáles son los pros y los contras entre los dos cuando se trata de rendimiento. Pero, no estaba preocupado por eso por ahora, porque usé esto para un script único para corregir datos incorrectos.
nemo
55
Segundo funcionó para mí :). Oracle es un animal fuerte pero extraño: /
elrado
10
Explicación sobre el requisito de clave preservada para uniones actualizables: asktom.oracle.com/pls/asktom/…
Vadzim
202

Utilizar este:

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
fuente
2
Funciona perfectamente, pero Oracle me pidió que lo dijera, merge into table 1 tetc.
Michael-O
1
Tarde a la fiesta, pero este sigue siendo un buen hilo. Necesito saber, aunque ... ¿me perdí algo? Tabla maestra, "tabla1". En USING, la tabla 1 se alias como t1. Tabla2, alias como t2, pero en ON, las referencias son ...? Tabla1 externa - no t1 - ¿es esto una referencia a la tabla externa o un tipo? ¿Tabla 2? ¿No es t2? Je suis confundido. Fan de mejores alias ...
Marc
Solo un punto aquí, si su clave (trg.rowid o src.rid) tiene un elemento duplicado, esta cláusula arroja un error: ora-30926.ora-code.com
Henrique
@Marc En ON, trgestá el alias de la tabla maestra (tabla table1"externa" según su lógica) y hace srcreferencia al USINGgrupo ("tabla interna" según su lógica). Pero sí, probablemente podría haber sido referenciado mejor, pero pude seguirlo.
vapcguy
1
@supernova: la respuesta de tony es actualizar una vista en línea. Esto puede funcionar en algunos casos, pero la vista tiene que estar "preservada por clave" (cada tabla unida debe estar unida por igualdad en su clave primaria o en un conjunto de campos único). Esto asegura que cada registro en la tabla de destino contribuya como máximo a un registro en el conjunto de filas resultante y, por lo tanto, cada registro en la tabla de destino se actualice como máximo una vez.
Quassnoi
25

MERGEcon WHEREcláusula:

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;

Necesita la WHEREcláusula porque las columnas a las que se hace referencia en la ONcláusula no se pueden actualizar.

Roland
fuente
Podría decirse que esta versión es más limpia, pero no es amigable con los activadores porque no tengo conocimiento de que evite activar activadores de actualización para filas sin cambios que utilizan esta sintaxis. (Estoy asumiendo que se necesitan los factores desencadenantes de las cambiado filas.)
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
fuente
11

No use algunas de las respuestas anteriores.

Algunos sugieren el uso de SELECT anidado, no lo hagas, es insoportablemente lento. Si tiene muchos registros para actualizar, use unirse, así que algo como:

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;

Vea este enlace para más detalles. http://geekswithblogs.net/WillSmith/archive/2008/06/18/oracle-update-with-join-again.aspx .

Además, asegúrese de que haya claves principales en todas las tablas a las que se une.

duvo
fuente
7

Como se indica aquí , la sintaxis general para la primera solución propuesta por Tony Andrews es:

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
                         )

Creo que esto es interesante, especialmente si desea actualizar más de un campo.

Alexis Dufrenoy
fuente
Esto no funciona para mi. Actualiza toda la tabla.
Natassia Tavares
3

La siguiente sintaxis funciona para mí.

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
fuente
@JimGarrison Vuelva a editar esta respuesta para que pueda eliminar mi voto negativo ... Estaba tratando de usar esta sintaxis y no estaba actualizando mi tabla. Descubrí por qué, mi SETestaba haciendo un REPLACEy estaba tratando de dejar una cadena en particular en la columna, resulta que Oracle trata ''como nulo, y este campo no se pudo anular. Pensé que la sintaxis era simplemente actualizar una tabla temporal en lugar de la real, pero estaba equivocado.
vapcguy
2

Usando la descripción en lugar de desc para 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
fuente
qué u desea disparar dos consultas separadas en la Tabla 2
Jitendra Vispute
2

Funciona bien oráculo

merge into table1 t1
using (select * from table2) t2
on (t1.empid = t2.empid)
when matched then update set t1.salary = t2.salary
user5299305
fuente
Puede establecer múltiples propiedades agregando una coma al final de eso. Tenía que hacerlo t1.First_Name = t2.FirstName, t1.Last_Name = t2.LastNameen una tabla después de hacerla coincidir en la columna "UserName" ( t1.UserName = t2.UserName) para recuperar su nombre de una tabla llamada UserInfo ( select * from UserInfo) t2). La base de datos era tal que usaba UserName como clave principal para UserInfo en todas partes, en lugar de colocar FirstName y LastName en la tabla, directamente. Esto solucionó eso!
vapcguy
Esta respuesta no agrega nada a la respuesta ya proporcionada por Quassnoi cinco años antes que la suya.
Forraje
0
UPDATE table1 t1
SET t1.value = 
    (select t2.CODE from table2 t2 
     where t1.value = t2.DESC) 
WHERE t1.UPDATETYPE='blah';
afnhsn
fuente
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`
usuario5685973
fuente
0

Solo como una cuestión de integridad, y como estamos hablando de Oracle, esto también podría hacerlo:

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
fuente
1
Esto podría hacerlo, pero se trata de la forma más lenta posible.
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 y B son campos de alias, no necesita señalar la tabla.

Dan Anderson
fuente
1
Hola dan Estás publicando una pregunta bastante antigua que ya tiene muy buenas respuestas. ¿Puede explicar cuándo su pregunta es preferible a las otras soluciones?
Noel Widmer
1
Por supuesto, he visto una respuesta donde b = a se escribió señalando el nombre de la tabla (table1.B = table2.A) pero no es necesario señalar la tabla.
Dan Anderson
En realidad, está actualizando campos desde la vista, que se asignan a la tabla. Si la vista interna tuviera un alias h, entonces la versión "autodocumentada" sería "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
             )
prueba
fuente