O PostgreSQL Upsert não funciona na tabela particionada

9

Tenha uma tabela como esta:

CREATE TABLE aggregated_master (
  "user"       BIGINT,
  type         TEXT,
  date         TIMESTAMP,
  operations   BIGINT,
  amount       NUMERIC,
  PRIMARY KEY ( "user", type, date )
);

Esta tabela é o mestre do qual muitas partições são herdadas. As partições são feitas por MONTH no campo DATE. Por exemplo: A partição para agosto de 2017 seria agg_201708 e a PK seria pk_agg_201708 Há o gatilho usual ANTES DE INSERIR para redirecionar a inserção para a partição apropriada.

A questão é que eu quero fazer um UPSERT nesta tabela. A parte DO CONFLICT não está funcionando.

O código primeiro era assim

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

Mas notei que a restrição (pk_aggregated) é a da tabela mestre, e não da tabela filha em que a inserção será realmente executada, devido ao gatilho.

Alterei a cláusula CONFLICT para:

ON CONFLICT (user, type, date)

Quais são os campos do PK, mas isso também não funciona.

Alguma idéia de como fazer isso funcionar?

Sergi Porta
fonte
2
Não pense que será devido a limitações na implementação. Realmente deve detectar isso e ERRO. Reportar um erro?
Craig Ringer
5
Veja também este tópico da lista de discussão postgresql.org/message-id/…
Craig Ringer

Respostas:

6

O PostgreSQL 11 suporta INSERT INTO ... ON CONFLICTcom tabelas particionadas:

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;

DBFiddle Demo


Limitação ddl-particionamento

5.10.2.3 Limitações

O uso da cláusula ON CONFLICT com tabelas particionadas causará um erro, pois restrições únicas ou de exclusão só podem ser criadas em partições individuais. Não há suporte para impor exclusividade (ou restrição de exclusão) em toda uma hierarquia de particionamento.

foi levantado.

lad2025
fonte
11

Upsert em tabelas particionadas não é implementado em versões anteriores ao Postgres 11.

No Postgres 9.6:

É improvável que as instruções INSERT com cláusulas ON CONFLICT funcionem conforme o esperado, pois a ação ON CONFLICT é executada apenas no caso de violações únicas na relação de destino especificada, e não nas relações filho.

O particionamento declarativo não resolve o problema, Postgres 10:

O uso da cláusula ON CONFLICT com tabelas particionadas causará um erro, pois restrições únicas ou de exclusão só podem ser criadas em partições individuais. Não há suporte para impor exclusividade (ou restrição de exclusão) em toda uma hierarquia de particionamento.

Gambiarra

No Postgres 11 você pode usar ON CONFLICTem tabelas particionadas, veja a resposta do lad2025.

klin
fonte
Esta resposta precisa ser atualizada para a página 11, que a implementa, como observa @ lad2025.
DB140141 3/01/19
@ DB140141 - thx, a resposta atualizada.
Klin