Oracle SQL: atualize uma tabela com dados de outra tabela

251

Tabela 1:

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

Mesa 2:

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

No oracle SQL, como executo uma consulta de atualização sql que pode atualizar a Tabela 1 com a Tabela 2 namee descusar a mesma id? Então o resultado final que eu obteria é

Tabela 1:

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

A pergunta é tirada da atualização de uma tabela com dados de outra , mas especificamente para o Oracle SQL.

Muhd
fonte
2
possível duplicata de consulta SQL UPDATE com dados de outra tabela
p.campbell
Você precisa voltar para sua outra pergunta, não aceitar essa resposta e declarar especificamente que precisa da sintaxe do Oracle PLSQL.
precisa saber é o seguinte
3
@ p.campbell, isso não é a minha pergunta ...
Muhd
1
Ah eu vejo. Então, você copiou o corpo da pergunta, mas modificou para incluir o bit Oracle.
precisa saber é o seguinte
2
Sim. E esse provavelmente não é o melhor exemplo, já que "desc" é uma palavra reservada, mas tudo bem.
Muhd

Respostas:

512

Isso é chamado de atualização 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 )

Supondo que a junção resulte em uma exibição preservada por chave, você também pode

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
fonte
8
No seu primeiro exemplo de código: a cláusula WHERE externa é necessária para resultados corretos? Ou você o usa apenas para acelerar a consulta?
Mathias Bader
41
@totoro - No primeiro exemplo, o WHERE EXISTSimpede de atualizar uma linha t1se não houver uma linha correspondente t2. Sem ele, todas as linhas t1serão atualizadas e os valores serão definidos como NULLse não houver nenhuma linha correspondente t2. Isso geralmente não é o que você deseja que aconteça, portanto WHERE EXISTSé geralmente necessário.
23613 Justin Caverna
3
Vale acrescentar que o resultado SELECT ... FROM t2 deve ser uma linha única. Isso significa que você deve selecionar em todos os campos que compreendem uma chave exclusiva - uma chave primária não exclusiva não é suficiente. Sem exclusividade, você é reduzido a algo como o loop de @ PaulKarr - e, se não houver uma correlação exclusiva, mais de uma linha de destino poderá ser atualizada para cada linha de origem.
Andrew Leach
2
Explicação sobre requisito preservado por chave para junções atualizáveis: asktom.oracle.com/pls/asktom/…
Vadzim
1
@RachitSharma - Isso significa que sua subconsulta (a consulta de table2) está retornando várias linhas para um ou mais table1valores e o Oracle não sabe qual você deseja usar. Normalmente, isso significa que você precisa refinar a subconsulta para que ela retorne uma única linha distinta.
Justin Cave.
132

Tente o seguinte:

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
fonte
4
Muito rápido na verdade, 1159477 linhas fundiram em 15,5s
jefissu
3
Espero que todos que visitam esta pergunta depois de 2015 tenham notado esta resposta. Observe que isso também funciona se table1e table2for a mesma tabela, apenas cuide da cláusula -part ONe da WHEREcláusula SELECT-st table2!
Sjngm 25/02/19
1
Acho que toda vez que preciso fazer outra fusão, volto sempre a esta resposta em busca de inspiração. Eu poderia imprimi-lo e enquadrá-lo na minha parede
arnehehe
Funciona como charme !! THX!
Davidwillianx
SELECT DISTINCT ID, FIELD1, FIELD1 FROM table2 ONDE O ID NÃO É NULO
Joseph Poirier
17

experimentar

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
fonte
4
A desvantagem disso é que a instrução SELECT é repetida 3 vezes. Em exemplos complexos, isso pode ser um diferencial.
David Balažic
9
Update table set column = (select...)

nunca funcionou para mim, pois o conjunto espera apenas 1 valor - Erro SQL: ORA-01427: a subconsulta de linha única retorna mais de uma linha.

aqui está a solução:

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;

É exatamente assim que você o executa na planilha SQLDeveloper. Eles dizem que é lento, mas essa é a única solução que funcionou para mim neste caso.

Pau Karr
fonte
alguém pode explicar por que isso merece -2 na reputação? RI MUITO.
Pau Karr
13
Não diminuí a taxa, mas não é uma boa solução. Primeiro: se a subseleção estava retornando vários valores, o loop for substituirá o nome na tabela2 várias vezes para alguns / todos os registros (não limpo). Em segundo lugar: não há ordem por cláusula, portanto, isso ocorrerá de maneira imprevisível (ou seja, o último valor em vitórias não ordenadas). Terceiro: será muito mais lento. Supondo que o resultado do loop for foi planejado, a subseleção original poderia ter sido reescrita de alguma maneira controlada para retornar apenas 1 valor para cada registro ... a maneira mais simples seria (selecione min (nome) ...)
Alternador
Era exatamente disso que eu precisava. Obrigado (+1)
Robert Hyatt
3
Se você obtiver vários valores em sua subconsulta, poderá repensar a consulta e usar DISTINCT ou GROUP BY com MIN, MAX. Apenas uma ideia.
Francis
Para encurtar a história: se você puder evitá-lo, NUNCA use NENHUM tipo de LOOP em uma instrução T-SQL. Pessoalmente, se não fosse pelo 0,001% do tempo em que não há outra solução, nem acho que deveria ser uma função disponível no T-SQL. O T-SQL foi projetado para ser baseado em conjuntos, para que ele funcione em conjuntos inteiros de dados como um todo; NÃO deve ser usado para trabalhar com dados linha por linha.
Ray K.
8

Aqui parece haver uma resposta ainda melhor com a cláusula 'in' que permite várias chaves para a junção :

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

A questão é ter as colunas que você deseja usar como chave entre parênteses na cláusula where antes de 'in' e ter a instrução select com os mesmos nomes de coluna entre parênteses. onde ( coluna1, coluna2 ) em ( selecione ( coluna1, coluna2 ) da tabela onde "o conjunto que eu quero" );

formiga
fonte
O link expirou. ( 404)
Dumbo
-3

Se sua tabela t1 e seu backup t2 tiverem muitas colunas, aqui está uma maneira compacta de fazer isso.

Além disso, meu problema relacionado era que apenas algumas das colunas foram modificadas e muitas linhas não tinham edições nessas colunas, então eu queria deixá-las em paz - basicamente restaure um subconjunto de colunas de um backup de toda a tabela. Se você quiser apenas restaurar todas as linhas, pule a cláusula where.

Claro que a maneira mais simples seria excluir e inserir como select, mas no meu caso eu precisava de uma solução com apenas atualizações.

O truque é que, quando você seleciona * de um par de tabelas com nomes de colunas duplicados, a segunda será denominada _1. Então, aqui está o que eu vim com:

  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
fonte
Isso não funciona para mim no Oracle 11g. Você pode criar um exemplo de trabalho desse 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
fonte