Oracle SQL: actualice una tabla con datos de otra tabla

251

Tabla 1:

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

Tabla 2:

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

En Oracle SQL, ¿cómo ejecuto una consulta de actualización SQL que puede actualizar la Tabla 1 con la Tabla 2 namey descusar la misma id? Entonces el resultado final que obtendría es

Tabla 1:

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

La pregunta se toma de la actualización de una tabla con datos de otra , pero específicamente para Oracle SQL.

Muhd
fuente
Debe volver a su otra pregunta, no aceptar esa respuesta y declarar específicamente que necesita la sintaxis de Oracle PLSQL.
p.campbell
3
@ p.campbell, esa no es mi pregunta ...
Muhd
1
Oh ya veo. Así que copió y pegó el cuerpo de la pregunta, pero lo modificó para incluir el bit de Oracle.
p.campbell
2
Si. Y este probablemente no sea el mejor ejemplo ya que "desc" es una palabra reservada, pero bueno.
Muhd

Respuestas:

512

Esto se llama actualización correlacionada

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 )

Suponiendo que la unión da como resultado una vista con clave preservada, también podría

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
Justin Cave
fuente
8
En su primer ejemplo de código: ¿Es necesaria la cláusula WHERE externa para obtener resultados correctos? ¿O lo usa solo para acelerar la consulta?
Mathias Bader
41
@totoro: en el primer ejemplo, le WHERE EXISTSimpide actualizar una fila t1si no hay una fila coincidente t2. Sin ella, cada fila t1se actualizará y los valores se establecerán en NULLsi no hay una fila coincidente t2. Eso generalmente no es lo que quiere que suceda, por lo WHERE EXISTSque generalmente se necesita.
Justin Cave
3
Vale la pena agregar que el resultado SELECT ... FROM t2 debe ser una fila única. Esto significa que debe seleccionar en todos los campos que comprenden una clave única; una clave primaria no única no es suficiente. Sin unicidad, se reduce a algo como el bucle de @ PaulKarr, y si no hay una correlación única, entonces se puede actualizar más de una fila de destino para cada fila de origen.
Andrew Leach
2
Explicación sobre el requisito de preservación de clave para uniones actualizables: asktom.oracle.com/pls/asktom/…
Vadzim
1
@RachitSharma: eso significa que su subconsulta (la consulta de table2) está devolviendo varias filas para uno o más table1valores y Oracle no sabe cuál desea usar. Normalmente, eso significa que necesita refinar la subconsulta para que devuelva una única fila distinta.
Justin Cave
132

Prueba esto:

MERGE INTO table1 t1
USING
(
-- For more complicated queries you can use WITH clause here
SELECT * FROM table2
)t2
ON(t1.id = t2.id)
WHEN MATCHED THEN UPDATE SET
t1.name = t2.name,
t1.desc = t2.desc;
Adrian
fuente
44
De hecho, muy rápido, 1159477 filas se fusionaron en 15,5s
jefissu
3
Espero que todos los que visiten esta pregunta después de 2015 se den cuenta de esta respuesta. Tenga en cuenta que esto también funciona si table1y table2son la misma tabla, solo tenga en cuenta la parte ONy la WHEREcláusula para la SELECTdeclaración de table2!
sjngm
1
Encuentro que cada vez que necesito hacer otra fusión sigo volviendo a esta respuesta en busca de inspiración. Podría imprimirlo y enmarcarlo en mi pared
arnehehe
Funciona como encanto !! ¡Gracias!
davidwillianx
SELECCIONE ID ID DISTINTO, CAMPO1, CAMPO1 DE la tabla2 DONDE LA IDENTIFICACIÓN NO ES NULA
Joseph Poirier
17

tratar

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);
Yahia
fuente
44
La desventaja de esto es que la instrucción SELECT se repite 3 veces. En ejemplos complejos que pueden ser un factor decisivo.
David Balažic
9
Update table set column = (select...)

nunca funcionó para mí, ya que set solo espera 1 valor - Error de SQL: ORA-01427: la subconsulta de una sola fila devuelve más de una fila.

Aquí está la solución:

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;

Así es exactamente como lo ejecuta en la hoja de trabajo SQLDeveloper. Dicen que es lento, pero esa es la única solución que me funcionó en este caso.

Pau Karr
fuente
¿Alguien puede explicar por qué esto merece un -2 en reputación? Jajaja
Pau Karr
13
No bajé la tasa, pero no es una buena solución. En primer lugar: si la subselección estaba devolviendo múltiples valores, entonces el bucle for sobrescribirá el nombre en table2 varias veces para algunos / todos los registros (no limpios). En segundo lugar: no hay orden por cláusula, por lo que esto ocurrirá de manera impredecible (es decir, el último valor en datos no ordenados gana). En tercer lugar: será mucho más lento. Suponiendo que se pretendía el resultado del ciclo for, la subselección original podría haberse reescrito de alguna manera controlada para devolver solo 1 valor para cada registro ... la forma más simple sería (seleccione min (nombre) ...)
Alternator
Esto era exactamente lo que necesitaba. Gracias (+1)
Robert Hyatt
3
Si obtiene múltiples valores en su subconsulta, puede repensar la consulta y usar DISTINCT o GROUP BY con MIN, MAX. Solo una idea.
Francis
En pocas palabras: si puede evitarlo, nunca use NINGÚN tipo de LOOP en una declaración T-SQL. Personalmente, si no fuera por el 0.001% del tiempo en que no hay otra solución, ni siquiera creo que deba ser una función disponible en T-SQL. T-SQL está diseñado para estar basado en conjuntos, por lo que funciona en conjuntos completos de datos en su conjunto; NO debe usarse para trabajar en datos línea por línea.
Ray K.
8

Aquí parece haber una respuesta aún mejor con la cláusula 'in' que permite múltiples claves para la unión :

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

El problema está en tener las columnas que desea utilizar como clave entre paréntesis en la cláusula where antes de 'in' y tener la instrucción select con los mismos nombres de columna entre paréntesis. where ( column1, column2 ) en ( select ( column1, column2 ) de la tabla donde "el conjunto que quiero" );

hormiga
fuente
El enlace ha expirado. ( 404)
Dumbo
-3

Si su tabla t1 y su copia de seguridad t2 tienen muchas columnas, aquí hay una forma compacta de hacerlo.

Además, mi problema relacionado era que solo algunas de las columnas se modificaron y muchas filas no tenían modificaciones en estas columnas, por lo que quería dejarlas en paz, básicamente restaurar un subconjunto de columnas a partir de una copia de seguridad de toda la tabla. Si solo desea restaurar todas las filas, omita la cláusula where.

Por supuesto, la forma más sencilla sería eliminar e insertar como select, pero en mi caso necesitaba una solución con solo actualizaciones.

El truco es que cuando selecciona * de un par de tablas con nombres de columna duplicados, la segunda se llamará _1. Así que esto es lo que se me ocurrió:

  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, ...
Jim P
fuente
Esto no funciona para mí en Oracle 11g. ¿Puedes crear un ejemplo funcional de este método?
Jon Heller
-3
BEGIN
For i in (select id, name, desc from table2) 
LOOP
Update table1 set name = i.name, desc = i.desc where id = i.id and (name is null or desc is null);
END LOOP;
END;
Avila Theresa
fuente