从多个表更新?

时间:2014-07-24 21:57:52

标签: sql postgresql join null sql-update

我正在尝试构建一个查询,其中我使用table_atable_b中包含的数据更新table_c

我发现的所有UPDATE ... FROM文档和问题似乎都专注于通过加入table_a来更新table_c 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

工作'一个表' -updates是:

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_aUPDATE ... FROM上的两个表格。

我可以用子查询来处理这个问题 - 但我更愿意避免这种情况。

2 个答案:

答案 0 :(得分:1)

为了缩短我的冗长答案,我使用表名abc
如果a.idb.idc.id UNIQUE (但可能会丢失):

基本形式,有很多警告(继续阅读!):

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

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

相关子查询

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

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)

仍然会浪费您尝试最小化的空更新。您可以为{...}添加WHERE子句EXISTS

答案 1 :(得分:0)

不是确切的查询,但更像是一个例子。您可以尝试如下,在内部查询中获取countedtable_b的{​​{1}}列,并在更新时将其加入table_b

table_a