Aggiornare una tabella utilizzando JOIN in SQL Server?


835

Voglio aggiornare una colonna in una tabella facendo un join su un'altra tabella, ad esempio:

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'

Ma si lamenta:

Messaggio 170, livello 15, stato 1, riga 2
riga 2: sintassi errata vicino a "a".

Cosa c'è che non va qui?

Risposte:


1598

Non hai la UPDATE FROMsintassi proprietaria di SQL Server inattiva. Inoltre, non sei sicuro del motivo per cui devi unirti a CommonFielde anche filtrarlo in seguito. Prova questo:

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 stai facendo qualcosa di veramente stupido - come cercare costantemente di impostare il valore di una colonna sull'aggregato di un'altra colonna (che viola il principio di evitare la memorizzazione di dati ridondanti), puoi usare un CTE (espressione di tabella comune) - vedi qui e qui per maggiori dettagli:

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

Il motivo per cui questo è davvero sciocco, è che dovrai rieseguire l'intero aggiornamento ogni volta ogni riga nelle table2modifiche. A SUMè qualcosa che puoi sempre calcolare in fase di esecuzione e, nel farlo, non devi mai preoccuparti che il risultato sia stantio.


4
Quando provo questo, non mi piace UPDATE table1 a SET a.[field] = b.[field] - rimuovere l'alias a funziona, quindiUPDATE table1 a SET [field] = b.[field]
baldmosher

@baldmosher Scommetto che c'è un altro problema, potresti pubblicare una riproduzione sul violino SQL?
Aaron Bertrand,

1
Non ha funzionato per me su MySQL. Ho dovuto usare la seguente (che ha più senso): UPDATE t1 INNER JOIN t2 on t2.col = t1.col SET t1.field=value WHERE t2.col=something.
George,

16
@GeorgeRappel ovviamente, probabilmente non funzionerà su molte altre piattaforme. La domanda riguarda SQL Server.
Aaron Bertrand,

Supponiamo che più record da t1 abbiano fatto riferimento allo stesso record da t2, quindi il join risulta nello stesso record t2 restituito in più righe. Nel tuo primo esempio, se invece aggiornassi t2, aggiornerebbe quel record più volte o solo una volta?
xr280xr,

46

Provalo in questo modo:

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

La risposta di cui sopra Aaron è perfetta:

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

Voglio solo aggiungere il motivo per cui questo problema si verifica in SQL Server quando proviamo a utilizzare l'alias di una tabella durante l'aggiornamento di quella tabella, di seguito la sintassi menzionerà sempre l'errore:

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

case può essere qualsiasi se si aggiorna una singola tabella o si aggiorna mentre si utilizza join.

Sebbene la query sopra funzionerà bene in PL / SQL ma non in SQL Server.

Il modo corretto per aggiornare una tabella durante l'utilizzo dell'alias di tabella in SQL Server è:

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

Spero che possa aiutare tutti perché l'errore è arrivato qui.


1
Bello grazie. La tua risposta è quella corretta per questa domanda.
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

Trovo utile trasformare un UPDATE in un SELECT per ottenere le righe che voglio aggiornare come test prima di aggiornare. Se riesco a selezionare le righe esatte che desidero, posso aggiornare solo quelle righe che voglio aggiornare.

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'

Altre alternative qui .


0

Un altro approccio sarebbe quello di utilizzare 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 fa parte dello standard SQL

-Anche sono abbastanza sicuro che gli aggiornamenti dei join interni non siano deterministici. Domanda simile qui in cui la risposta parla di questo http://ask.sqlservercentral.com/questions/19089/updating-two-tables-using-single-query. html


4
Sebbene possano essere standard, starei molto attentoMERGE .
Aaron Bertrand,

1
Il che è tutto molto divertente perché letteralmente 5 minuti dopo aver pubblicato questo mi sono imbattuto in alcuni aggiornamenti problematici non deterministici negli sprocs che ho ereditato :-) cose divertenti
Shane Neuville

Ciò non migliora l'unione, significa solo che hai aggiornamenti errati.
Aaron Bertrand,

1
Sì, ero solo un aneddoto :-) Avevo questo nel cervello quando mi sono immerso di nuovo nello sproc ed è stata la prima cosa che ho visto.
Shane Neuville,

3
I CTE sono standard; le parentesi quadre per sfuggire ai nomi sciocchi non lo sono (le doppie virgolette sono).
giorno

0

Ho avuto lo stesso problema .. e non è necessario aggiungere una colonna fisica .. perché ora dovrai mantenerla .. quello che puoi fare è aggiungere una colonna generica nella query di selezione:

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

L'approccio di Aaron sopra ha funzionato perfettamente per me. La mia dichiarazione di aggiornamento era leggermente diversa perché avevo bisogno di unirmi sulla base di due campi concatenati in una tabella per abbinare un campo in un'altra tabella.

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

Provare:

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

7
Sto effettuando il downvoting, perché questo aggiornerà ogni riga table1, non solo le righe in cui c'è una corrispondenza sul campo comune tra entrambe le tabelle (effettivamente un join sinistro e non un join interno).
Cᴏʀʏ

@ Cᴏʀʏ: Vuoi dire che aggiornerà ogni corrispondenza delle righe BatchNo = '110', giusto? Tutti i downgrade sono stati il ​​risultato di questo effetto o altri hanno avuto altri motivi per il voto negativo?
Palswim,

Chiedo perché alcuni potrebbero accettare che l'operazione UPDATE possa impostare alcune delle righe su NULLe questo modulo potrebbe essere una soluzione specifica T-SQL meno.
Palswim,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.