web-dev-qa-db-fra.com

Mettre à jour une table en utilisant JOIN dans SQL Server?

Je souhaite mettre à jour une colonne dans une table en créant une jointure sur une autre table, par exemple:

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'

Mais il se plaint:

Msg 170, Niveau 15, Etat 1, Ligne 2
Ligne 2: syntaxe incorrecte près de 'a'.

Qu'est-ce qui ne va pas ici?

729
Manjot

La syntaxe UPDATE FROM propriétaire de SQL Server n’est pas utilisée. Également vous ne savez pas pourquoi vous avez besoin de rejoindre la CommonField et de filtrer par la suite. Essaye ça:

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

Si vous faites quelque chose de vraiment stupide - comme essayer constamment de définir la valeur d'une colonne sur l'agrégat d'une autre colonne (ce qui viole le principe consistant à éviter de stocker des données redondantes), vous pouvez utiliser un CTE (expression de table commune) - voir ici et ici pour plus de détails:

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

La raison pour laquelle c'est vraiment stupide, c'est que vous allez devoir réexécuter toute la mise à jour à chaque fois que l'une des lignes de table2 change. Une SUM est quelque chose que vous pouvez toujours calculer au moment de l'exécution et, ce faisant, ne vous inquiétez jamais du fait que le résultat est périmé.

1422
Aaron Bertrand

Essayez comme ça:

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

La réponse donnée ci-dessus par Aaron est parfaite:

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

Je veux juste ajouter pourquoi ce problème se produit dans SQL Server lorsque nous essayons d'utiliser l'alias d'une table lors de la mise à jour de cette table. La syntaxe de mention ci-dessous donnera toujours l'erreur:

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

le cas peut être indifférent si vous mettez à jour une seule table ou mettez à jour en utilisant join.

Bien que la requête ci-dessus fonctionne correctement dans PL/SQL mais pas dans SQL Server.

La méthode correcte pour mettre à jour une table lors de l'utilisation de l'alias de table dans SQL Server est la suivante:

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

J'espère que cela aidera tout le monde pourquoi l'erreur est venue ici.

27
Ankur Bhutani
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";
3
onedaywhen
    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'

Plus d'alternatives ici: http://www.karlrixon.co.uk/writing/update-multiple-rows-with-different-values-and-a-single-sql-query/

2
wut

On dirait que SQL Server 2012 peut également gérer l'ancienne syntaxe de mise à jour de Teradata:

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

Si je me souviens bien, 2008R2 donnait une erreur lorsque j'ai essayé une requête similaire.

2
nyunyu

Je trouve utile de transformer un UPDATE en SELECT pour obtenir les lignes que je veux mettre à jour à titre de test avant la mise à jour. Si je peux sélectionner les lignes exactes que je veux, je ne peux mettre à jour que les lignes que je veux mettre à jour.

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

Une autre approche serait d’utiliser 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 fait partie du standard SQL

-Aussi, je suis à peu près sûr que les mises à jour des jointures internes ne sont pas déterministes. Question similaire ici où la réponse parle de cela http://ask.sqlservercentral.com/questions/19089/updating-two-tables-using- single-query.html

0
Shane Neuville

J'ai eu le même problème .. et vous n'avez pas besoin d'ajouter une colonne physique .. car maintenant vous devrez la maintenir ... Ce que vous pouvez faire est d'ajouter une colonne générique dans la requête select:

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