Atualizar uma tabela usando JOIN no SQL Server?


835

Eu quero atualizar uma coluna em uma tabela fazendo uma junção em outra tabela, por exemplo:

UPDATE table1 a 
INNER JOIN table2 b ON a.commonfield = b.[common field] 
SET a.CalculatedColumn= b.[Calculated Column]
WHERE 
    b.[common field]= a.commonfield
AND a.BatchNO = '110'

Mas está reclamando:

Msg 170, nível 15, estado 1, linha 2
linha 2: sintaxe incorreta próxima a 'a'.

O que há de errado aqui?

Respostas:


1598

Você não tem a UPDATE FROMsintaxe proprietária do SQL Server desativada. Também não sei por que você precisou ingressar no CommonFielde também filtrá-lo posteriormente. Tente o seguinte:

UPDATE t1
  SET t1.CalculatedColumn = t2.[Calculated Column]
  FROM dbo.Table1 AS t1
  INNER JOIN dbo.Table2 AS t2
  ON t1.CommonField = t2.[Common Field]
  WHERE t1.BatchNo = '110';

Se você está fazendo algo realmente bobo - como tentar constantemente definir o valor de uma coluna para o agregado de outra coluna (que viola o princípio de evitar o armazenamento de dados redundantes), você pode usar uma CTE (expressão de tabela comum) - veja aqui e aqui para mais detalhes:

;WITH t2 AS
(
  SELECT [key], CalculatedColumn = SUM(some_column)
    FROM dbo.table2
    GROUP BY [key]
)
UPDATE t1
  SET t1.CalculatedColumn = t2.CalculatedColumn
  FROM dbo.table1 AS t1
  INNER JOIN t2
  ON t1.[key] = t2.[key];

A razão pela qual isso é realmente bobo é que você precisará executar novamente toda essa atualização toda vez que qualquer linha nas table2alterações. A SUMé algo que você sempre pode calcular em tempo de execução e, ao fazer isso, nunca precisa se preocupar que o resultado seja obsoleto.


3
Quando eu tento isso, ele não gosta UPDATE table1 a SET a.[field] = b.[field] - removendo a um alias de funcionar, por issoUPDATE table1 a SET [field] = b.[field]
baldmosher

@baldmosher Aposto que há outra questão, você poderia postar uma repro no violino do SQL?
Aaron Bertrand

1
Não funcionou para mim no MySQL. Eu tive que usar o seguinte (que faz mais sentido): UPDATE t1 INNER JOIN t2 on t2.col = t1.col SET t1.field=value WHERE t2.col=something.
George

15
@GeorgeRappel, é claro, provavelmente também não funcionará em muitas outras plataformas. A questão é sobre o SQL Server.
Aaron Bertrand

Digamos que vários registros de t1 referenciem o mesmo registro de t2, para que a junção resulte no mesmo registro t2 retornado em várias linhas. No seu primeiro exemplo, se você atualizasse t2, ele atualizaria esse registro várias vezes ou apenas uma vez?
Xl280xr

46

Tente assim:

begin tran
    UPDATE a 
    SET a.CalculatedColumn= b.[Calculated Column]
    FROM table1 a INNER JOIN table2 b ON a.commonfield = b.[common field] 
    WHERE a.BatchNO = '110'
commit tran

29

A resposta dada acima por Aaron é perfeita:

UPDATE a
  SET a.CalculatedColumn = b.[Calculated Column]
  FROM Table1 AS a
  INNER JOIN Table2 AS b
  ON a.CommonField = b.[Common Field]
  WHERE a.BatchNo = '110';

Só quero adicionar por que esse problema ocorre no SQL Server quando tentamos usar o alias de uma tabela durante a atualização dessa tabela; a sintaxe abaixo mencionada sempre dará erro:

update tableName t 
set t.name = 'books new' 
where t.id = 1

case pode ser qualquer um, se você estiver atualizando uma única tabela ou atualizando enquanto estiver usando junção.

Embora a consulta acima funcione bem no PL / SQL, mas não no SQL Server.

A maneira correta de atualizar uma tabela ao usar o alias da tabela no SQL Server é:

update t 
set t.name = 'books new' 
from tableName t 
where t.id = 1

Espero que ajude a todos por que o erro veio aqui.


Bom obrigado. Sua resposta é a correta para esta pergunta.
Ola Ström

4
MERGE table1 T
   USING table2 S
      ON T.CommonField = S."Common Field"
         AND T.BatchNo = '110'
WHEN MATCHED THEN
   UPDATE
      SET CalculatedColumn = S."Calculated Column";


2

Acho útil transformar um UPDATE em um SELECT para obter as linhas que quero atualizar como teste antes de atualizar. Se eu puder selecionar as linhas exatas que eu quero, eu posso atualizar apenas as linhas que eu quero atualizar.

DECLARE @expense_report_id AS INT
SET @expense_report_id = 1027

--UPDATE expense_report_detail_distribution
--SET service_bill_id = 9

SELECT *
FROM expense_report_detail_distribution erdd
INNER JOIN expense_report_detail erd
INNER JOIN expense_report er 
    ON er.expense_report_id = erd.expense_report_id 
    ON erdd.expense_report_detail_id = erd.expense_report_detail_id
WHERE er.expense_report_id = @expense_report_id

2
    UPDATE mytable
         SET myfield = CASE other_field
             WHEN 1 THEN 'value'
             WHEN 2 THEN 'value'
             WHEN 3 THEN 'value'
         END
    From mytable
    Join otherTable on otherTable.id = mytable.id
    Where othertable.somecolumn = '1234'

Mais alternativas aqui .


0

Outra abordagem seria usar MERGE

  ;WITH cteTable1(CalculatedColumn, CommonField)
  AS
  (
    select CalculatedColumn, CommonField from Table1 Where BatchNo = '110'
  )
  MERGE cteTable1 AS target
    USING (select "Calculated Column", "Common Field" FROM dbo.Table2) AS source ("Calculated Column", "Common Field")
    ON (target.CommonField = source."Common Field")
    WHEN MATCHED THEN 
        UPDATE SET target.CalculatedColumn = source."Calculated Column";

-Merge faz parte do padrão SQL

-Também tenho certeza de que as atualizações de junção interna não são determinísticas. Pergunta semelhante aqui onde a resposta fala sobre isso http://ask.sqlservercentral.com/questions/19089/updating-two-tables-using-single-query. html


3
Embora possam ser padrão, eu seria muito cuidadosoMERGE .
Aaron Bertrand

1
Que é todo o tipo de engraçado, porque literalmente 5 minutos depois que eu postei isso me deparei com algumas atualizações não-deterministas problemáticos nas sprocs eu herdei :-) Fun Stuff
Shane Neuville

Isso não melhora a mesclagem, apenas significa que você tem atualizações ruins.
Aaron Bertrand

1
Sim, eu estava apenas sendo anedótico :-) Eu tinha isso no cérebro quando mergulhei de volta no broche e foi a primeira coisa que vi.
Shane Neuville

2
CTEs são padrão; colchetes para escapar de nomes tolos não são (aspas duplas são).
onedaywhen

0

Eu tive o mesmo problema .. e você não precisa adicionar uma coluna física .. porque agora você terá que mantê-la .. o que você pode fazer é adicionar uma coluna genérica na consulta de seleção:

EX:

select tb1.col1, tb1.col2, tb1.col3 ,
( 
select 'Match' from table2 as tbl2
where tbl1.col1 = tbl2.col1 and tab1.col2 = tbl2.col2
)  
from myTable as tbl1

0

A abordagem de Aaron acima funcionou perfeitamente para mim. Minha declaração de atualização foi um pouco diferente porque eu precisava ingressar com base em dois campos concatenados em uma tabela para corresponder a um campo em outra tabela.

 --update clients table cell field from custom table containing mobile numbers

update clients
set cell = m.Phone
from clients as c
inner join [dbo].[COSStaffMobileNumbers] as m 
on c.Last_Name + c.First_Name = m.Name

-3

Tentar:

UPDATE table1
SET CalculatedColumn = ( SELECT [Calculated Column] 
                         FROM table2 
                         WHERE table1.commonfield = [common field])
WHERE  BatchNO = '110'

6
Estou com voto negativo, porque isso atualizará todas as linhas table1, não apenas as linhas em que há uma correspondência no campo comum entre as duas tabelas (efetivamente uma junção esquerda e não uma junção interna).
Cᴏʀʏ

@ Cᴏʀʏ: Você quer dizer que ele atualizará todas as linhas correspondentes BatchNo = '110', certo? Todas as votações negativas ocorreram como resultado desse efeito ou outras pessoas tiveram outras razões para votar negativamente?
Palswim 21/05/19

Pergunto porque alguns podem aceitar que a operação UPDATE pode definir algumas das linhas como NULL, e esse formulário pode ser uma solução específica menos para T-SQL.
Palswim 21/05/19
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.