【问题标题】:Behavior of update with left join左连接的更新行为
【发布时间】:2013-11-27 18:46:43
【问题描述】:

使用mysql 5.5,语句如下:

update foo left join bar on foo.id=bar.id set foo.col='val', bar.col='val';

即使没有 bar 也确实会更新 foo 行(并且 bar.col= 不会生成任何错误或警告)。但据我所知,这在 mysql 文档中并没有得到保证。

任何人都可以提供权威参考,无论是在 SQL 标准中还是来自 Oracle,说明这是故意的?

它在其他风格的 SQL,特别是 postgres 和 MariaDB 或其他版本的 mysql 中是否同样有效?

请注意,与上面不同的是,以下内容确实将左连接提升为内连接:

update foo left join bar on foo.id=bar.id set bar.col='val';

但这可能只是一种优化。

更新:澄清一下,我看到的行为就是我想要的行为;我只是担心在我看来它没有证件。如果我想要一个内部连接,我会使用一个。

【问题讨论】:

  • 您无法在给出答案后更改问题,从而使合法答案看起来偏离主题。我回滚了你上次的编辑。
  • 到目前为止没有人回答我的实际问题,尽管您已经向我澄清它根本不适用于 PostgreSQL,我对此表示赞赏。 IMO 不提及 postgres 就足以证明您已经给出了答案;我把它放回去(并赞成你的回答)。
  • 嗯,cmets 用来澄清。一般来说,以一种会使给定答案无效的方式改变问题的性质是行不通的。
  • 来自dev.mysql.com/doc/refman/5.0/en/update.html 页面的For the multiple-table syntax, UPDATE updates rows in each table named in table_references that satisfy the conditions. 不保证为您提供此功能吗?
  • @AgRizzo:差不多,但不完全

标签: mysql sql-update left-join mariadb


【解决方案1】:

在 PostgreSQL 中,这种奇怪的语法是完全无效的。 UPDATE statement 只会直接更改单个表中的数据(继承除外)。 你可以改用:

UPDATE foo
SET    col = 'val'
FROM   bar
WHERE  foo.id = bar.id;

或者,由于您使用的是常量值,因此这种更快的形式:

UPDATE foo
SET    col = 'val'
WHERE  EXISTS (
   SELECT 1
   FROM   bar
   WHERE  foo.id = bar.id
   );

.. 应该在命名的 RDMS(MySQL、SQL Server、Oracle 等)的任何中工作。

这些查询中的任何一个更新foo 的行,其中在bar 中找到匹配项。通常,明智的做法是添加:

AND col IS DISTINCT FROM 'val'

或者:

AND col <> 'val'                  -- if involved values are NOT NULL

.. 避免空更新。

【讨论】:

  • 谢谢,这清楚地表明我的问题根本不适用于 PostgreSQL。
【解决方案2】:

这是一个左连接,所以你看到的是预期的。

  • A cross join B 表示:每个 A 与每个 B 结合——就像在笛卡尔积中一样。

  • A inner join B,或简称为A join B,表示:每个 A 和 B 结合在一起,其中有一个 A 一个 B — 如,强制同时有 A 和B.

  • A left join B 表示:没有 B 的每个 A,以及有 A 和 B 的每个 A 和 B 组合 — 如 B 可选

  • A right join B 等价于B left join A

  • A full join B 表示:没有 B 的每个 A,没有 A 的每个 B,以及可以组合的每个 A 和 B — 如 A B 可选。

http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html

您的第二个查询更新与第一个查询相同的 bar 行。与第一个的唯一区别是您不再更新foo,因此您可能看不到相同的受影响行数。只有在有关联的foo 时才更新bar,但请注意,查询仍会遍历没有bar 关联的foo 行。

回答您问题的另一部分:是的,其他数据库引擎的行为方式相同,给予或接受与 Erwin 强调的语法相关的修饰。更新语句的要点相当于伪代码,如下所示:for each row in (select … from …) do stuffupdate table 中的表是关联的 select … from … [join …] 语句中第一个运行的表。

【讨论】:

  • 那么为什么bar.col='val' 在第一种情况下不将 bar 设为非可选?
  • @ysth:我的错,我错过了set 声明。我已经相应地更新了答案。
  • 这并没有真正回答我问题的第一部分,它只是描述了我已经看到的行为。无论如何,谢谢。
  • @ysth:我不确定您对问题的第一部分还需要什么……这是对连接如何工作的简单解释。或者您只是想要一个wikipedia reference 和一个oracle reference ANSI 连接?
  • 对于仅左连接的表的列,集合 col= 的存在是什么?我喜欢我看到的行为,但我可以争辩说它应该使左连接有效地成为内连接,或者生成警告,甚至是错误。我不是要求解释,我要求的是现有行为实际上得到保证的文档。
猜你喜欢
  • 1970-01-01
  • 2020-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多