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

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


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;

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.


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

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.


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.


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;

@ 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'
;

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

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

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`

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

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.


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
             )
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.