【问题标题】:PostgreSQL CTE UPDATE-FROM query skips rowsPostgreSQL CTE UPDATE-FROM 查询跳过行
【发布时间】:2019-09-10 13:39:09
【问题描述】:

2张桌子

table_1 行:注意:id 2 有两行

-----------------------
| id | counts | track |
-----------------------
| 1  | 10     | 1     |
| 2  | 10     | 2     |
| 2  | 10     | 3     |
-----------------------

table_2 行

---------------
| id | counts |
---------------
| 1  | 0      |
| 2  | 0      |
---------------

查询:

with t1_rows as (
    select id, sum(counts) as counts, track 
    from table_1
    group by id, track
)
update table_2 set counts = (coalesce(table_2.counts, 0) + t1.counts)::float 
from t1_rows t1
where table_2.id = t1.id;

select * from table_2;

当我运行上面的查询时,我得到 table_2 输出

---------------
| id | counts |
---------------
| 1  | 10     |
| 2  | 10     | (expected counts as 20 but got 10)
---------------

我注意到上面的更新查询只考虑第一个匹配并跳过休息。

我可以通过更改如下查询使其工作。现在 table_2 按预期更新,因为 table_1 没有重复的行。

但我想知道为什么我之前的查询不起作用。有什么问题吗?

with t1_rows as (
    select id, sum(counts) as counts, array_agg(track) as track 
    from table_1
    group by id
)
update table_2 set counts = (coalesce(table_2.counts, 0) + t1.counts)::float 
from t1_rows t1
where table_2.id = t1.id;

架构

CREATE TABLE IF NOT EXISTS table_1(
  id varchar not null,
  counts integer not null,
  track integer not null
);

CREATE TABLE IF NOT EXISTS table_2(
  id varchar not null,
  counts integer not null
);

insert into table_1(id, counts, track) values(1, 10, 1), (2, 10, 2), (2, 10, 3);
insert into table_2(id, counts) values(1, 0), (2, 0);

【问题讨论】:

    标签: sql postgresql common-table-expression


    【解决方案1】:

    问题在于 PostgreSQL 中的 UPDATE 会创建行的新版本,而不是就地更改行,但新行版本在当前查询的快照中不可见。因此,从查询的角度来看,该行在第一次更新时“消失”了。

    The documentation 说:

    当存在FROM 子句时,本质上发生的情况是目标表与<strong><em>from_list</em></strong> 中提到的表连接,并且连接的每个输出行都代表目标表的更新操作。使用FROM 时,您应该确保连接最多为要修改的每一行生成一个输出行。换句话说,目标行不应连接到来自其他表的多个行。如果是这样,那么只有一个连接行将用于更新目标行,但将使用哪一个是不容易预测的。

    【讨论】:

    • 那么在这种情况下使用 CTE UPDATE-FROM 是不是很糟糕?
    • 我试图回答您的问题:但我想知道为什么我之前的查询不起作用。 使用 CTE 没有问题,但请确保使用FROM 子句连接只返回每个目标行一次。
    【解决方案2】:

    因此,如果我正确阅读了您的问题,您希望 table_1 中的第 2 行和第 3 行加在一起吗?如果是这样,您的第一种方法不起作用的原因是因为它grouped by id, track. 由于第 2 行和第 3 行在 track 列中有不同的数字,因此它们没有被 group by 子句加在一起。

    您的第二种方法有效,因为它仅按 id 分组

    【讨论】:

    • 实际上第一个确实有效,只是没有像您预期的那样,因为 CTE 为 #2 生成了 2 行,更新使用相同的值执行了两次。
    猜你喜欢
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-24
    • 2021-02-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多