【问题标题】:Update from multiple tables?从多个表更新?
【发布时间】:2014-09-16 15:47:14
【问题描述】:

我正在尝试构建一个查询,在该查询中我使用table_btable_c 中包含的数据更新table_a

我发现的所有UPDATE ... FROM 文档和问题似乎都集中在使用table_c 中的数据更新table_a,方法是通过table_b 加入。我不希望这样,我需要使用table_btable_c 中包含的数据,它们都与table_a 保持连接

一个示例选择是:

SELECT 
        a.id 
    ,   b.counted as count_b
    ,   c.counted as count_c
FROM 
    table_a a
LEFT JOIN
    table_b b on a.id = b.id
LEFT JOIN
    table_c c on a.id = c.id
;


 id | count_b | count_c
----+---------+---------
 34 |       6 |       2
 43 |      16 |       2
 45 |      33 |       5

工作“一个表”-更新是:

UPDATE table_a SET count_b = table_b.counted
FROM table_b WHERE table_a.id = table_b.id;

UPDATE table_a SET count_c = table_c.counted
FROM table_c WHERE table_a.id = table_c.id;

但是我想将它们结合起来以提高效率(更新是插入+删除,因此可以节省一些钱。但我似乎无法弄清楚如何将两个表连接到 table_a 中一个UPDATE ... FROM

我可以使用子查询来处理这个问题 - 但我宁愿避免这样做。

【问题讨论】:

  • 有影响的重要问题:a.idb.idc.id 独特吗?他们每个人都可以失踪吗? countedcount_b等可以NULL吗?
  • b.id 和 c.id 都是 a.id 的子集;可能不会有 b.id 或 c.id(这就是我使用左连接的原因)。

标签: sql postgresql join null sql-update


【解决方案1】:

为了缩短我冗长的答案,我使用表名 abc
如果a.idb.idc.idUNIQUE(但可以缺失):

基本形式和一些注意事项(继续阅读!):

UPDATE a
SET    count_b = b.counted
     , count_c = c.counted
FROM   b
FULL   JOIN c USING (id)
WHERE  a.id = COALESCE(b.id, c.id)
;

如果任何 id 列不是 UNIQUE,则无法正常工作 - 但也不会单独更新。

如果没有任何实际变化(所有 counted 列与 a 中的内容相同),您只需创建一个死行即可获得全部成本。如果是这种情况并且您想避免这种情况,请附加一个额外的 WHERE 子句:

AND   (a.count_b <> b.counted OR
       a.count_c <> c.counted)  -- simple form with all columns NOT NULL

FULL [OUTER] JOIN 使其工作,如果 一些,但不是所有 表(一个两个 源表的简单情况下)不有一行相同的id。它有效地模拟了您想到的多个LEFT JOIN,但有一个小但重要的区别:对于缺少行的表中的列,您会得到幻像NULL 值。用COALESCE 抓住它:

SET    count_b = COALESCE(b.counted, a.count_b)

如果counted 可以是NULL仍然存在一个极端情况。 COALESCE分不清两种情况:
1.b.counted IS NULL.
2.b.counted不存在。
如果您需要区分,请改用CASE 语句:

SET    count_b = CASE WHEN b.id IS NULL THEN a.count_b ELSE b.counted END

相应地调整 WHERE 子句。
完整版,涵盖所有NULL 案例,避免空更新:

UPDATE a
SET    count_b = CASE WHEN b.id IS NULL THEN a.count_b ELSE b.counted END
     , count_c = CASE WHEN c.id IS NULL THEN a.count_b ELSE c.counted END
FROM   b
FULL   JOIN c USING (id)
WHERE  a.id = COALESCE(b.id, c.id)
AND   (b.id IS NOT NULL AND b.counted IS DISTINCT FROM a.count_b OR
       c.id IS NOT NULL AND c.counted IS DISTINCT FROM a.count_c)
;

看起来很复杂,但如果源表之间存在大量重叠,应该会更快。

相关子查询

如果任何源表中没有匹配的ida 中的相应行更新,这通常是您想要的。无论如何,您都可以使用相关子查询来更新所有行 - 这通常是您不想要的,但您的情况可能会有所不同。对于大表而言,相关子查询通常也更昂贵。

UPDATE a
SET    count_b = (SELECT b.counted FROM b WHERE b.id = a.id)
     , count_c = (SELECT c.counted FROM c WHERE c.id = a.id)

同样,您可以通过以下方式避免 NULL 值:

SET    count_b = COALESCE ((SELECT b.counted ...), count_b)

仍然会浪费您试图最小化的空更新。您可以为此添加带有EXISTSWHERE 子句...

【讨论】:

  • 哇。谢谢你。您回答了所有问题,并考虑了所有问题。我从样本中取出合并以避免混淆,但没有考虑NULLCASE。在我的情况下,它将为 0。确实存在大量重叠 - 我实际上正在更新用于处理分析的 6 个不同临时表中的 6 个字段。
【解决方案2】:

不是确切的查询,但更像是一个示例。您可以尝试如下方式,方法是在内部查询中从table_btable_b 中获取counted 列,并在更新中将其加入table_a

UPDATE table_a 
SET count_b = tableb_counted,
count_c = tablec_counted
FROM (
  SELECT table_b.counted as tableb_counted,
  table_b.id,
  table_c.counted as tablec_counted
  FROM table_b
  JOIN table_c
  ON table_b.id = table_c.id
) tab
WHERE table_a.id = tab.id 

【讨论】:

  • INNER JOIN 将排除缺少 (b, c) 之一的行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-22
  • 1970-01-01
  • 2013-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多