Atualizar instrução com junção interna no Oracle

298

Eu tenho uma consulta que funciona bem no MySQL, mas quando a executo no Oracle, recebo o seguinte erro:

Erro SQL: ORA-00933: O comando SQL não foi finalizado corretamente
00933. 00000 - "Comando SQL não foi finalizado corretamente"

A consulta é:

UPDATE table1
INNER JOIN table2 ON table1.value = table2.DESC
SET table1.value = table2.CODE
WHERE table1.UPDATETYPE='blah';
user169743
fonte
Quando tentei configurar a table2 no Oracle para testar minha resposta, descobri que o Oracle rejeitava o DESC como um nome de coluna.
Janek Bogucki
Desculpe eu só abreviada do nome da coluna original para desconto a sua, obviamente, não que no db
user169743

Respostas:

412

Essa sintaxe não é válida no Oracle. Você consegue fazer isso:

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 você pode fazer isso:

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 se a visualização em linha for considerada atualizável pelo Oracle ( para ser atualizável para a segunda instrução depende de algumas regras listadas aqui ).

Tony Andrews
fonte
5
Eu fiz o segundo exemplo, mas teve que adicionar aliases para os nomes das colunas na selecionar e, em seguida, referenciá-los por seus nomes no conjunto, mas funcionou, graças
Gustavo Rubio
41
O segundo exemplo tem o benefício de permitir que você teste o SQL antes de realmente executar a atualização.
Daniel Reis
10
O segundo exemplo funcionou para mim. Eu gosto desse porque parece limpo e legível. Não sei quais são os prós e os contras entre os dois quando se trata de desempenho. Mas, por enquanto, não estava preocupado com isso, porque usei isso em um script único para corrigir dados incorretos.
Nemo 02/05
5
Segundo trabalhou para mim :). O Oracle é um animal forte, mas estranho: /
elrado
10
Explicação sobre requisito preservado por chave para junções atualizáveis: asktom.oracle.com/pls/asktom/…
Vadzim
202

Usa isto:

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
fonte
2
Funciona perfeitamente, mas a Oracle exigia que eu dissesse merge into table 1 te assim por diante.
Michael-O
1
Tarde para a festa, mas ainda é um bom tópico. Eu preciso saber, tho '... eu perdi alguma coisa? Tabela mestre, "tabela1". No USING, tabela1 alias como t1. Tabela2, alias de t2, mas no ON, as referências são ...? Tabela externa1 - não t1 - é uma referência à tabela externa ou a um tipo? Mesa 2? Não é T2? Je suis confuso. Fan de melhores aliases ...
Marc
Apenas um ponto aqui, se sua chave (trg.rowid ou src.rid) tem um item duplicado esta cláusula lançar um erro: ora-30926.ora-code.com
Henrique
@ Marc No ON, trgé o alias da tabela principal (tabela table1"externa" pela sua lógica) e faz srcreferência ao USINGgrupo ("tabela interna" pela sua lógica). Mas sim, provavelmente poderia ter sido melhor referenciado, mas eu fui capaz de segui-lo.
vapcguy
1
@ supernova: a resposta de tony está atualizando uma visualização embutida. Isso pode funcionar em alguns casos, mas a exibição precisa ser "preservada por chave" (todas as tabelas unidas precisam ser unidas por igualdade em sua chave primária ou em outro conjunto de campos exclusivo). Isso garante que todos os registros na tabela de destino contribuam para no máximo um registro no conjunto de linhas resultante e, portanto, todos os registros na tabela de destino sejam atualizados no máximo uma vez.
Quassnoi
25

MERGEcom a 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;

Você precisa da WHEREcláusula porque as colunas referenciadas na ONcláusula não podem ser atualizadas.

Roland
fonte
Esta versão é indiscutivelmente mais limpa, mas não é acionável, porque não há nenhuma maneira de evitar disparar gatilhos de atualização para linhas inalteradas usando esta sintaxe. (Estou assumindo que os gatilhos são necessários para os mudaram linhas.)
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
fonte
11

Não use algumas das respostas acima.

Alguns sugerem o uso de SELECT aninhado, não faça isso, é terrivelmente lento. Se você tiver muitos registros para atualizar, use join, então 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;

Veja este link para mais detalhes. http://geekswithblogs.net/WillSmith/archive/2008/06/18/oracle-update-with-join-again.aspx .

Além disso, verifique se há chaves primárias em todas as tabelas nas quais você está ingressando.

duvo
fonte
7

Conforme indicado aqui , a sintaxe geral para a primeira solução proposta por Tony Andrews é:

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
                         )

Eu acho que isso é interessante, especialmente se você deseja atualizar mais de um campo.

Alexis Dufrenoy
fonte
Isso não funciona para mim. Atualiza a tabela inteira.
Natassia Tavares
3

Esta sintaxe a seguir funciona para mim.

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
fonte
@ JimGarrison Re-edite esta resposta para que eu possa remover meu voto negativo .... Eu estava tentando usar essa sintaxe e ela não estava atualizando minha tabela. Eu descobri o porquê - eu SETestava fazendo um REPLACEe estava tentando colocar em branco uma sequência específica na coluna - resulta que o Oracle trata ''como nulo, e esse campo não pôde ser anulado. Eu pensei que a sintaxe estava apenas atualizando uma tabela temporária em vez da real, mas eu estava errado.
vapcguy
2

Usando a descrição em vez de desc para a tabela2,

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
fonte
porque vc quiser disparar duas consultas separadas em table2
Jitendra Vispute
2

Funciona bem 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
fonte
Pode definir várias propriedades adicionando uma vírgula no final disso. Eu precisava fazer t1.First_Name = t2.FirstName, t1.Last_Name = t2.LastNameem uma tabela depois de correspondê-la na coluna "UserName" ( t1.UserName = t2.UserName) para recuperar o nome de uma tabela chamada UserInfo ( select * from UserInfo) t2). O banco de dados era o local onde estava usando UserName como uma chave primária para UserInfo em todos os lugares, em vez de colocar FirstName e LastName na tabela, diretamente. Isso consertou isso!
vapcguy
Esta resposta não acrescenta nada à resposta já fornecida por Quassnoi cinco anos antes da sua.
Forragem
0
UPDATE table1 t1
SET t1.value = 
    (select t2.CODE from table2 t2 
     where t1.value = t2.DESC) 
WHERE t1.UPDATETYPE='blah';
afnhsn
fonte
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
fonte
0

Por uma questão de integridade, e porque estamos falando do Oracle, isso também pode ser feito:

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
fonte
1
Isso poderia ser feito, mas é a maneira mais lenta possível.
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 e B são campos de alias, você não precisa apontar a tabela.

Dan Anderson
fonte
1
Oi Dan. Você está postando uma pergunta bastante antiga que já tem respostas muito boas. Você pode explicar quando você pergunta é preferível a outras soluções?
quer
1
Obviamente, vi uma resposta em que b = a foram escritos apontando o nome da tabela (tabela1.B = tabela2.A), mas não há necessidade de apontar a tabela.
Dan Anderson
Na verdade, você está atualizando os campos da exibição, que são mapeados para a tabela. Se a visão interna fosse alias h, a versão "auto-documentada" seria "set hb = ha".
sf_jeff 29/09/19
-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
             )
teste
fonte