【问题标题】:Update a table using JOIN in SQL Server?在 SQL Server 中使用 JOIN 更新表?
【发布时间】:2010-12-08 21:54:32
【问题描述】:

我想更新一个表中的一列,在其他表上进行连接,例如:

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'

但它在抱怨:

消息 170,第 15 级,状态 1,第 2 行
第 2 行:'a' 附近的语法不正确。

这里有什么问题?

【问题讨论】:

    标签: sql-server tsql join sql-update


    【解决方案1】:

    您还没有完全掌握 SQL Server 专有的 UPDATE FROM 语法。也不确定为什么您需要加入CommonField 并在之后对其进行过滤。试试这个:

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

    如果您正在做一些愚蠢的事情 - 比如不断尝试将一列的值设置为另一列的聚合(这违反了避免存储冗余数据的原则),您可以使用 CTE(通用表表达式) -请参阅herehere 了解更多详情:

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

    这样做很愚蠢的原因是,每次table2 中的任何行发生更改时,您都必须重新运行整个更新。 SUM 是您始终可以在运行时计算的东西,这样做时,永远不必担心结果过时。

    【讨论】:

    • 当我尝试这个时,它不喜欢 UPDATE table1 a SET a.[field] = b.[field] -- 删除 a 别名确实有效,所以 UPDATE table1 a SET [field] = b.[field]
    • @baldmosher 我敢打赌还有另一个问题,你能在 SQL fiddle 上发布一个 repro 吗?
    • 在 MySQL 上对我不起作用。我不得不使用以下内容(这更有意义):UPDATE t1 INNER JOIN t2 on t2.col = t1.col SET t1.field=value WHERE t2.col=something.
    • @GeorgeRappel 当然,可能也无法在许多其他平台上运行。问题是关于 SQL Server。
    • 假设来自 t1 的多条记录引用了来自 t2 的相同记录,因此连接会导致在多行中返回相同的 t2 记录。在您的第一个示例中,如果您改为更新 t2,它会更新该记录多次还是只更新一次?
    【解决方案2】:

    试试这样:

        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'
    

    【讨论】:

    • 为什么要使用事务?
    • @CervEd 因为原始问题中有交易,我只是修复了 OP 自己的代码,然后几年后有人无缘无故地编辑了它。关于 StackOverflow 的一个坏事是人们对原始问题进行不必要的编辑只是为了让他们获得编辑徽章,但这些无车的编辑会使已经发布的答案看起来很奇怪甚至荒谬。
    • 谢谢,我不知道 OPs 问题已被编辑,所以这让我想知道是否有特殊原因使用对我来说并不明显的事务
    【解决方案3】:

    Aaron 上面给出的答案是完美的:

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

    只是想补充一下,当我们在更新表时尝试使用表的别名时,为什么会在 SQL Server 中出现这个问题,下面提到的语法总是会出错:

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

    如果您要更新单个表或在使用联接时更新,则可以是任何情况。

    虽然上述查询在 PL/SQL 中可以正常工作,但在 SQL Server 中则不行。

    在 SQL Server 中使用表别名更新表的正确方法是:

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

    希望对大家为什么会出现错误有所帮助。

    【讨论】:

    • 很好,谢谢。你的答案是这个问题的正确答案。
    【解决方案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";
    

    【讨论】:

      【解决方案5】:

      似乎 SQL Server 2012 也可以处理 Teradata 的旧更新语法:

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

      如果我没记错的话,当我尝试类似的查询时,2008R2 会出错。

      【解决方案6】:

      我发现将 UPDATE 转换为 SELECT 以在更新之前获取我想要更新的行作为测试很有用。如果我可以选择我想要的确切行,我可以只更新我想要更新的那些行。

      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
      

      【讨论】:

        【解决方案7】:
            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'
        

        更多选择here.

        【讨论】:

          【解决方案8】:

          另一种方法是使用 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 是 SQL 标准的一部分

          -我也很确定内部连接更新是不确定的.. 类似的问题在这里答案谈到了 http://ask.sqlservercentral.com/questions/19089/updating-two-tables-using-single-query.html

          【讨论】:

          • 虽然它们可能是标准的,但 I'd be very careful with MERGE.
          • 这很有趣,因为在我发布这篇文章 5 分钟后,我在继承的存储过程中发生了一些有问题的非确定性更新 :-) 有趣的东西
          • 这并没有让合并变得更好,它只是意味着你有错误的更新。
          • 是的,我只是在说轶事 :-) 当我回到存储过程中时,我脑子里就有这个,这是我看到的第一件事。
          • CTE 是标准的;方括号来逃避愚蠢的名字不是(双引号是)。
          【解决方案9】:

          我想,这就是你要找的。​​p>

          UPDATE
              Table1
          SET
              Table1.columeName =T1.columeName * T2.columeName
          FROM 
              Table1 T1
              INNER JOIN Table2 T2
                  ON T1.columeName = T2.columeName;
          

          【讨论】:

            【解决方案10】:

            我遇到了同样的问题..您不需要添加物理列..因为现在您必须维护它.. 您可以做的是在选择查询中添加一个通用列:

            前:

            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
            

            【讨论】:

              【解决方案11】:

              上述亚伦的方法对我来说非常有效。我的更新语句略有不同,因为我需要基于连接在一个表中的两个字段来连接以匹配另一个表中的字段。

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

              【讨论】:

                【解决方案12】:

                使用MYSQL的人

                UPDATE table1 INNER JOIN table2 ON table2.id = table1.id SET table1.status = 0 WHERE table1.column = 20
                

                【讨论】:

                  【解决方案13】:

                  试试:

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

                  【讨论】:

                  • 我投反对票,因为这将更新table1 中的每一行,而不仅仅是两个表之间的公共字段匹配的行(实际上是左加入而不是内部联接)。
                  • @Cᴏʀʏ:你的意思是它会更新每一行匹配BatchNo = '110',对吧?是否所有的反对票都是由于这种影响,还是其他人有其他反对票的原因?
                  • 我问是因为有些人可能会接受 UPDATE 操作可能会将某些行设置为NULL,而这种形式可能是一个不太针对 T-SQL 的解决方案。
                  猜你喜欢
                  • 1970-01-01
                  • 2020-03-20
                  • 1970-01-01
                  • 2022-01-17
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多