【问题标题】:Why/how does this ambiguous UPDATE statement work?这个模棱两可的 UPDATE 语句为什么/如何工作?
【发布时间】:2010-10-05 23:12:56
【问题描述】:

假设您正在对一个表运行 UPDATE 语句,但您放入此基表的信息来自其他一些辅助表。通常,您会 JOIN 数据,而不是期望 UPDATE 语句的 FROM 子句中的行相乘,从而保持一个新行映射到基表中的一个旧行。

但我想知道如果您的 JOIN 表以某种方式模棱两可会发生什么,就像您无法将每个基本实体映射到一个连接实体一样。或者,如果您做了一些荒谬的事情,例如将基表连接到其子表的表,并使用该信息更新基表。它会如何选择?现在每个基表行有多个行。

我在 SQL Server 2005 中运行了这样的语句,它似乎选择了每组中的第一行。但这对我来说似乎是错误的。它不应该引发错误吗?为什么这是理想的行为?

示例代码

-- normal
-- categories are one-to-many bundles

update bundles_denormalized set category = c.description

from bundles_denormalized b
left join categories c
on b.category_id = c.id

-- ambiguous
-- bundles are one-to-many products

update bundles_denormalized set category = p.description

from bundles_denormalized b
left join products p
on b.id = p.bundle_id

【问题讨论】:

    标签: sql database sql-server-2005 theory


    【解决方案1】:

    我实际上只是注意到它在我的情况下做了一些非常愚蠢的事情。我的意图是创建一个单行选择,但显然有我没想到的重复项。在这种情况下,它实际上将混合数据输入到目标行中,从第一个源行中选择一些列,从第二个源行中选择一些列。

    我很确定 Firebird 会抛出异常,如果试图做一些像这样模棱两可的事情。但是Firebird根本不支持(非标准?)sntax update X from X join Y...

    【讨论】:

      【解决方案2】:

      From BOL on UPDATE

      在 FROM 子句中使用 UPDATE

      UPDATE 语句的结果是 如果语句包含一个未定义的 中未指定的 FROM 子句 这样只有一个值是 可用于每列出现 即已更新,即如果 UPDATE 陈述不是确定性的。 例如,在 UPDATE 语句中 在以下脚本中,两行 表1 符合条件的 UPDATE 语句中的 FROM 子句; 但未定义来自哪一行 Table1 用于更新中的行 表2。

      USE AdventureWorks;
      GO
      IF OBJECT_ID ('dbo.Table1', 'U') IS NOT NULL
          DROP TABLE dbo.Table1;
      GO
      IF OBJECT_ID ('dbo.Table2', 'U') IS NOT NULL
          DROP TABLE dbo.Table2;
      GO
      CREATE TABLE dbo.Table1 
          (ColA int NOT NULL, ColB decimal(10,3) NOT NULL);
      GO
      CREATE TABLE dbo.Table2 
          (ColA int PRIMARY KEY NOT NULL, ColB decimal(10,3) NOT NULL);
      GO
      INSERT INTO dbo.Table1 VALUES(1, 10.0);
      INSERT INTO dbo.Table1 VALUES(1, 20.0);
      INSERT INTO dbo.Table2 VALUES(1, 0.0);
      GO
      UPDATE dbo.Table2 
      SET dbo.Table2.ColB = dbo.Table2.ColB + dbo.Table1.ColB
      FROM dbo.Table2 
          INNER JOIN dbo.Table1 
          ON (dbo.Table2.ColA = dbo.Table1.ColA);
      GO
      SELECT ColA, ColB 
      FROM dbo.Table2;
      

      换句话说,它是一种有效的语法,不会抛出错误或异常。

      但同时您不能确定更新值将是 FROM 子句中的第一条或最后一条记录,因为它没有定义。

      【讨论】:

      • 重要的是要注意声明结果未知的部分。尽管在 OPs 示例中始终使用“第一个”结果,但这并不能保证。 SQL Server 可以按任何顺序进行更新。
      • Dag nabbit,我也打开了那篇文章。没有检查备注部分。赢家!
      • 需要注意的是不同版本的SQL会有不同的反应。虽然 SQL 2005 通常会使用第一行,但旧版本的 SQL 通常会使用最后一行。
      【解决方案3】:

      实际上,如果我正确理解了这个问题,它会多次更新该字段,只是因为只有一条记录,所以它最终只有一个值。为什么不会出错?因为语法是正确的,并且数据库无法知道您的意图是什么。你想这样做吗?通常情况下,这就是为什么您应该在运行更新之前选择更新以确保正确记录获得正确的值。

      我通常用这种方式写更新:

      update b    
      set category = p.description
      --select b.category, p.description
      from bundles_denormalized b
      left join products p on b.id = p.bundle_id
      

      我也会对在更新中使用左连接保持警惕,因为您可能会将值更改为空值。如果这是你想要的,那没关系,但如果不是,那就不行了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-21
        • 2018-05-28
        • 1970-01-01
        • 2011-08-01
        • 1970-01-01
        • 2023-03-24
        相关资源
        最近更新 更多