Eine Tabelle mit JOIN in SQL Server aktualisieren?

835

Ich möchte eine Spalte in einer Tabelle aktualisieren, die einen Join für eine andere Tabelle erstellt, z.

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'

Aber es beschwert sich:

Meldung 170, Ebene 15, Status 1, Zeile 2
Zeile 2: Falsche Syntax in der Nähe von 'a'.

Was ist hier falsch?

Manjot
quelle

Antworten:

1598

Die proprietäre UPDATE FROMSyntax von SQL Server ist nicht ganz ausgefallen . Auch nicht sicher, warum Sie sich dem anschließen CommonFieldund danach auch filtern mussten. Versuche dies:

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

Wenn Sie etwas wirklich Dummes tun - beispielsweise ständig versuchen, den Wert einer Spalte auf das Aggregat einer anderen Spalte zu setzen (was gegen das Prinzip verstößt, das Speichern redundanter Daten zu vermeiden), können Sie einen CTE (Common Table Expression) verwenden - siehe hier und hier für weitere Details:

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

Der Grund, warum dies wirklich albern ist, ist, dass Sie das gesamte Update jedes Mal neu ausführen müssen, wenn table2Änderungen vorgenommen werden. A SUMkönnen Sie immer zur Laufzeit berechnen und müssen sich dabei keine Sorgen machen, dass das Ergebnis veraltet ist.

Aaron Bertrand
quelle
3
Wenn ich das versuche, mag es nicht UPDATE table1 a SET a.[field] = b.[field] - das Entfernen des Alias ​​funktioniert, alsoUPDATE table1 a SET [field] = b.[field]
Baldmosher
@baldmosher Ich wette, es gibt noch ein anderes Problem. Könnten Sie einen Repro auf SQL Fiddle posten?
Aaron Bertrand
1
Hat bei MySQL nicht funktioniert. Ich musste folgendes verwenden (was sinnvoller ist) : UPDATE t1 INNER JOIN t2 on t2.col = t1.col SET t1.field=value WHERE t2.col=something.
George
15
@ GeorgeRappel funktioniert natürlich wahrscheinlich auch nicht auf vielen anderen Plattformen. Die Frage betrifft SQL Server.
Aaron Bertrand
Angenommen, mehrere Datensätze von t1 haben auf denselben Datensatz von t2 verwiesen, sodass der Join zu demselben t2-Datensatz führt, der in mehreren Zeilen zurückgegeben wird. Wenn Sie in Ihrem ersten Beispiel stattdessen t2 aktualisieren, wird dieser Datensatz dann mehrmals oder nur einmal aktualisiert?
xr280xr
46

Versuchen Sie es so:

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
RBarryYoung
quelle
29

Die oben von Aaron gegebene Antwort ist perfekt:

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

Ich möchte nur hinzufügen, warum dieses Problem in SQL Server auftritt, wenn wir versuchen, beim Aktualisieren dieser Tabelle den Alias ​​einer Tabelle zu verwenden. Die unten erwähnte Syntax führt immer zu Fehlern:

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

Dies kann der Fall sein, wenn Sie eine einzelne Tabelle aktualisieren oder während der Verwendung von join aktualisieren.

Die obige Abfrage funktioniert zwar einwandfrei in PL / SQL, jedoch nicht in SQL Server.

Die richtige Methode zum Aktualisieren einer Tabelle unter Verwendung des Tabellenalias in SQL Server ist:

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

Hoffe, es wird allen helfen, warum hier Fehler aufgetreten sind.

Ankur Bhutani
quelle
Nett, danke. Ihre Antwort ist die richtige für diese Frage.
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";
eines Tages, wenn
quelle
3

Anscheinend kann SQL Server 2012 auch die alte Update-Syntax von Teradata verarbeiten:

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

Wenn ich mich richtig erinnere, gab 2008R2 einen Fehler aus, als ich eine ähnliche Abfrage versuchte.

Nyunyu
quelle
2

Ich finde es nützlich, ein UPDATE in ein SELECT umzuwandeln, um die Zeilen, die ich aktualisieren möchte, vor dem Aktualisieren als Test abzurufen. Wenn ich genau die gewünschten Zeilen auswählen kann, kann ich nur die Zeilen aktualisieren, die ich aktualisieren möchte.

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
CW1255
quelle
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'

Weitere Alternativen hier .

wut
quelle
0

Ein anderer Ansatz wäre die Verwendung von 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 ist Teil des SQL-Standards

-Auch ich bin mir ziemlich sicher, dass innere Join-Updates nicht deterministisch sind. Ähnliche Frage hier, wo die Antwort darüber spricht http://ask.sqlservercentral.com/questions/19089/updating-two-tables-using-single-query. html

Shane Neuville
quelle
3
Obwohl sie Standard sein mögen, würde ich sehr vorsichtig seinMERGE .
Aaron Bertrand
1
Was alles irgendwie lustig ist, weil ich buchstäblich 5 Minuten nachdem ich dies gepostet habe, auf einige problematische nicht deterministische Updates in den Sprocs gestoßen bin, die ich geerbt habe :-) lustiges Zeug
Shane Neuville
Das macht das Zusammenführen nicht besser, es bedeutet nur, dass Sie schlechte Updates haben.
Aaron Bertrand
1
Ja, ich war nur anekdotisch :-) Ich hatte das im Gehirn, als ich zurück in den Sproc tauchte und es war das erste, was ich sah.
Shane Neuville
2
CTEs sind Standard; eckige Klammern, um dummen Namen zu entkommen, sind nicht (doppelte Anführungszeichen).
Tag, wenn
0

Ich hatte das gleiche Problem. Und Sie müssen keine physische Spalte hinzufügen. Denn jetzt müssen Sie sie pflegen. Sie können lediglich eine generische Spalte in die Auswahlabfrage einfügen:

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
Mahmoud sagte
quelle
0

Aarons Ansatz oben hat perfekt für mich funktioniert. Meine Update-Anweisung war etwas anders, da ich basierend auf zwei in einer Tabelle verketteten Feldern beitreten musste, um mit einem Feld in einer anderen Tabelle übereinzustimmen.

 --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
Jeremy Boutot
quelle
-3

Versuchen:

UPDATE table1
SET CalculatedColumn = ( SELECT [Calculated Column] 
                         FROM table2 
                         WHERE table1.commonfield = [common field])
WHERE  BatchNO = '110'
user140301
quelle
6
Ich stimme ab, weil dadurch jede Zeile aktualisiert wirdtable1 , nicht nur die Zeilen, in denen das gemeinsame Feld zwischen beiden Tabellen übereinstimmt (effektiv ein linker Join und kein innerer Join).
29.
@ Cᴏʀʏ: Du meinst, dass es jede übereinstimmende Zeile aktualisiert BatchNo = '110', oder? Sind alle Abstimmungen auf diesen Effekt zurückzuführen, oder hatten andere andere Gründe für eine Abwertung?
Palswim
Ich frage, weil einige akzeptieren können, dass die UPDATE-Operation einige der Zeilen auf setzt NULL, und dieses Formular möglicherweise eine weniger T-SQL-spezifische Lösung ist.
Palswim