UPDATE的FROM端如何与UPDATE的目标表相关联?

时间:2015-08-12 12:03:39

标签: sql postgresql join sql-update

以下查询(来自此处Postgres SQL SELECT and UPDATE behaving differently

update fromemailaddress
    set call = true 
    from email
    where email.fromemailaddress = fromemailaddress.fromemailaddress and
          LOWER(email.subject) ~ 'tester';

我读它的方式是:

Line 1: update fromemailaddress

- 我们告诉数据库我们正在更新fromemailaddress表

Line 2:        set call = true 

- 我们告诉数据库名为" call"将设置为true

Line 3:        from email
Line 4:         where email.fromemailaddress = fromemailaddress.fromemailaddress and
Line 5:              LOWER(email.subject) ~ 'tester';

- 现在好了,事情变得模糊了。这里到底发生了什么?似乎数据库以某种方式在第3行和第5行中获取查询结果,但是它如何告诉它在fromemailaddress表中更新哪些行?什么是伪码?它是这样的:

for each row in (query from lines 3, 4, 5)
    set call=true?

我无法看到SQL更新的FROM端如何与另一方相关。

更新:

按照下面@ Erwin的回答中的有价值的链接,我将获得这些信息,这些信息是我试图理解的核心信息:

http://www.postgresql.org/docs/current/interactive/sql-update.html

  

当存在FROM子句时,基本上发生的是   目标表连接到from_list中提到的表,和   连接的每个输出行代表一个更新操作   目标表。使用FROM时,应确保连接生成   每个要修改的行最多一个输出行。换句话说,一个   目标行不应该从另一行连接到多个行   表(一个或多个)。如果是,则只使用其中一个连接行   更新目标行,但不会轻易使用哪一行   可预测的。

     

由于这种不确定性,仅在其中引用其他表   子选择更安全,但通常更难阅读和慢   使用连接。

2 个答案:

答案 0 :(得分:1)

您显示的UPDATE查询与以下内容完全相同:

UPDATE fromemailaddress f
SET    call = true 
FROM  (
   SELECT fromemailaddress
   FROM   email 
   WHERE  subject ILIKE '%tester%'
   ) e
WHERE  e.fromemailaddress = f.fromemailaddress;

subject ILIKE '%tester%'subject ~ 'tester'相比更快。 LIKEILIKE和正则表达式匹配(~in the manual的详细信息或dba.SE上的相关答案:

有效地 与:

相同
UPDATE fromemailaddress f
SET    call = true
WHERE  EXISTS (
   SELECT 1
   FROM   email e
   WHERE  e.fromemailaddress = f.fromemailaddress
   AND    e.subject ILIKE '%tester%'
   );

改为使用它。

如果表email中有多个行与fromemailaddress中的行匹配相同fromemailaddress,则此表单只执行 一个 每行更新,与您不幸的原始版本不同。

不要因为fromemailaddress在这里被用作列和表名这一事实而感到困惑。

请仔细阅读本手册herehere。特别是这一点:

  

<强> from_list

     

表表达式列表,允许其他表中的列   出现在WHERE条件和更新表达式中。这是   类似于FROM Clause中可以指定的表格列表   一个SELECT声明。请注意,目标表不得出现在    from_list ,除非您打算自行加入(在这种情况下必须   在 from_list 中显示别名。

答案 1 :(得分:1)

这是一个内部联接。记录仅在email.fromemailaddress = fromemailaddress.fromemailaddress处更新。如果它不是一对一关系,则email中的一条记录将更新fromemailaddress中的多条记录,或email中的多条记录将更新fromemailaddress中的单个记录。

前者很好。如果您从源表中写入值,那么后者是非确定性的。由于查询没有定义订单,因此无法保证结果。它将以查询引擎选择的任何顺序完成。例如:

update fromemailaddress
set call = email.call
from email
where email.fromemailaddress = fromemailaddress.fromemailaddress and
      LOWER(email.subject) ~ 'tester';

现在,如果电子邮件/ fromemailaddress关系是多对一的,那么您可能会遇到问题。

由于此特定查询始终将值设置为静态值,因此没有确定性问题。