¿Actualizar una tabla usando JOIN en SQL Server?


835

Quiero actualizar una columna en una tabla haciendo una unión en otra tabla, por ejemplo:

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'

Pero se está quejando:

Mensaje 170, Nivel 15, Estado 1, Línea 2
Línea 2: Sintaxis incorrecta cerca de 'a'.

¿Que esta mal aquí?

Respuestas:


1598

No tiene la UPDATE FROMsintaxis propietaria de SQL Server desactivada. Tampoco estoy seguro de por qué necesitaba unirse CommonFieldy luego filtrarlo. Prueba esto:

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 está haciendo algo realmente tonto, como tratar constantemente de establecer el valor de una columna en el agregado de otra columna (que viola el principio de evitar el almacenamiento de datos redundantes), puede usar un CTE (expresión de tabla común), consulte aquí y aquí para más detalles:

;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 razón por la que esto es realmente tonto, es que tendrá que volver a ejecutar esta actualización completa cada vez que haya una fila en los table2cambios. A SUMes algo que siempre se puede calcular en tiempo de ejecución y, al hacerlo, nunca tiene que preocuparse de que el resultado sea obsoleto.


44
Cuando intento esto, no me gusta UPDATE table1 a SET a.[field] = b.[field] : eliminar el alias sí funciona, así queUPDATE table1 a SET [field] = b.[field]
calvo el

@baldmosher Apuesto a que hay otro problema, ¿podría publicar una reproducción en el violín de SQL?
Aaron Bertrand

1
No funcionó para mí en MySQL. Tuve que usar la siguiente (que tiene más sentido): UPDATE t1 INNER JOIN t2 on t2.col = t1.col SET t1.field=value WHERE t2.col=something.
George

16
@GeorgeRappel, por supuesto, probablemente tampoco funcionará en muchas otras plataformas. La pregunta es sobre SQL Server.
Aaron Bertrand

Digamos que varios registros de t1 hacen referencia al mismo registro de t2, por lo que la unión da como resultado que el mismo registro de t2 se devuelva en varias filas. En su primer ejemplo, si actualizara t2, ¿actualizaría ese registro varias veces o solo una vez?
xr280xr

46

Pruébalo así:

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 respuesta dada por Aaron es perfecta:

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

Solo quiero agregar por qué ocurre este problema en SQL Server cuando tratamos de usar el alias de una tabla mientras se actualiza esa tabla, la siguiente sintaxis siempre dará error:

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

el caso puede ser cualquiera si está actualizando una sola tabla o actualizando mientras usa join.

Aunque la consulta anterior funcionará bien en PL / SQL pero no en SQL Server.

La forma correcta de actualizar una tabla mientras se usa el alias de tabla en SQL Server es:

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

Espero que ayude a todos por qué el error vino aquí.


1
Bien gracias. Su respuesta es la correcta para esta pregunta.
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

Me resulta útil convertir una ACTUALIZACIÓN en una SELECCIÓN para obtener las filas que quiero actualizar como prueba antes de actualizar. Si puedo seleccionar las filas exactas que quiero, puedo actualizar solo las filas que quiero actualizar.

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'

Más alternativas aquí .


0

Otro enfoque sería 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 es parte del estándar SQL

-También estoy bastante seguro de que las actualizaciones de unión interna no son deterministas. Pregunta similar aquí, donde la respuesta habla sobre eso http://ask.sqlservercentral.com/questions/19089/updating-two-tables-using-single-query. html


44
Si bien pueden ser estándar, sería muy cuidadoso con esoMERGE .
Aaron Bertrand

1
Lo cual es bastante divertido porque, literalmente, 5 minutos después de que publiqué esto, encontré algunas actualizaciones problemáticas no deterministas en los sprocs que heredé :-) cosas divertidas
Shane Neuville

Eso no mejora la combinación, solo significa que tienes malas actualizaciones.
Aaron Bertrand

1
Sí, solo estaba siendo anecdótico :-) Tenía esto en el cerebro cuando volví a sumergirme en el sproc y fue lo primero que vi.
Shane Neuville

3
Los CTE son estándar; los corchetes para escapar de nombres tontos no lo son (las comillas dobles son).
cuando

0

Tuve el mismo problema ... y no necesita agregar una columna física ... porque ahora tendrá que mantenerlo ... lo que puede hacer es agregar una columna genérica en la consulta de selección:

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

El enfoque de Aaron anterior funcionó perfectamente para mí. Mi declaración de actualización fue ligeramente diferente porque necesitaba unirme en base a dos campos concatenados en una tabla para que coincida con un campo en otra tabla.

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

Tratar:

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

77
Estoy votando a favor, porque esto actualizará cada fila table1, no solo las filas donde hay una coincidencia en el campo común entre ambas tablas (efectivamente, una unión izquierda y no una unión interna).
Cᴏʀʏ

@ Cᴏʀʏ: ¿Quiere decir que actualizará cada coincidencia de fila BatchNo = '110', verdad? ¿Todos los votos negativos se produjeron como resultado de este efecto, u otros tuvieron otros motivos para rechazarlos?
palswim

Pregunto porque algunos pueden aceptar que la operación ACTUALIZACIÓN puede establecer algunas de las filas NULL, y este formulario puede ser una solución menos específica de T-SQL.
palswim
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.