PostgreSQL Upsert 不适用于分区表

Ser*_*rta 10 postgresql plpgsql upsert

有一张这样的表:

CREATE TABLE aggregated_master (
  "user"       BIGINT,
  type         TEXT,
  date         TIMESTAMP,
  operations   BIGINT,
  amount       NUMERIC,
  PRIMARY KEY ( "user", type, date )
);
Run Code Online (Sandbox Code Playgroud)

这张表是很多分区继承的主表。分区由 DATE 字段中的 MONTH 完成。例如:2017 年 8 月的分区将是 agg_201708,它的 PK 将是 pk_agg_201708 在插入之前通常会触发将插入重定向到正确的分区。

问题是我想在这个表中做一个 UPSERT。DO CONFLICT 部分不起作用。

最初的代码是这样的

INSERT INTO aggregated_master (user, type, date, oeprations, amount)
SELECT user, type, date, SUM(ops), SUM(amt)
FROM ...
WHERE ...
GROUP BY USER, TYPE, DATE
ON CONFLICT ON CONSTRAINT pk_aggregated
DO UPDATE SET operations = EXCLUDED.operations
          ,   amount = EXCLUDED.amount
Run Code Online (Sandbox Code Playgroud)

但是后来我注意到约束 (pk_aggregated) 是主表上的约束,而不是由于触发器而在真正执行插入的子表上的约束。

我将条款 CONFLICT 更改为:

ON CONFLICT (user, type, date)
Run Code Online (Sandbox Code Playgroud)

哪些是 PK 的字段,但这也不起作用。

知道如何进行这项工作吗?

kli*_*lin 13

在 Postgres 11 之前的版本中未实现分区表上的 Upsert。

在 Postgres 9.6 中:

带有 ON CONFLICT 子句的 INSERT 语句不太可能按预期工作,因为 ON CONFLICT 操作仅在对指定目标关系而非其子关系发生唯一违规的情况下才会执行。

声明式分区不能解决问题,Postgres 10:

对分区表使用 ON CONFLICT 子句将导致错误,因为唯一或排除约束只能在单个分区上创建。不支持在整个分区层次结构中强制执行唯一性(或排除约束)。

解决方法

在 Postgres 11 中,您可以ON CONFLICT在分区表上使用,请参阅lad2025 的回答。


小智 8

PostgreSQL 11 支持INSERT INTO ... ON CONFLICT分区表:

CREATE TABLE o(id INT PRIMARY KEY, i INT) PARTITION BY RANGE (id);

CREATE TABLE o1 PARTITION OF o FOR VALUES FROM (1) TO (1000);
CREATE TABLE o2 PARTITION OF o FOR VALUES FROM (1000) TO (2000);

INSERT INTO o(id, i) VALUES (1,1),(2,2),(1500,1500);

INSERT INTO o(id, i)
VALUES (1500, 1400), (2,20), (3, 3)
ON CONFLICT (id)
DO UPDATE SET i = EXCLUDED.i;

SELECT * FROM o;
Run Code Online (Sandbox Code Playgroud)

DBFiddle 演示


限制ddl 分区

5.10.2.3. 限制

对分区表使用 ON CONFLICT 子句将导致错误,因为唯一或排除约束只能在单个分区上创建。不支持在整个分区层次结构中强制执行唯一性(或排除约束)。

已被解除。