PostgreSQL Upsert no funciona en la tabla particionada

9

Tener una mesa como esta:

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

Esta tabla es el maestro del que heredan muchas particiones. Las particiones se realizan por MES en el campo FECHA. Por ejemplo: la partición para agosto de 2017 sería agg_201708 y su PK sería pk_agg_201708. Existe el desencadenante habitual ANTES DE INSERTAR para redirigir la inserción a la partición adecuada.

Lo que pasa es que quiero hacer un UPSERT en esta tabla. La parte DO CONFLICT no funciona.

El código primero fue así

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

Pero luego noté que la restricción (pk_aggregated) es la que está en la tabla maestra, y no en la tabla secundaria donde realmente se realizará la inserción, debido al disparador.

Cambié la cláusula CONFLICT a:

ON CONFLICT (user, type, date)

Cuáles son los campos de la PK, pero esto tampoco funciona.

¿Alguna idea de cómo hacer que esto funcione?

Sergi Porta
fuente
2
No piense que será debido a limitaciones en la implementación. Realmente debería detectar eso y ERROR. ¿Reportar un error?
Craig Ringer el
55
Vea también este hilo de la lista de correo postgresql.org/message-id/…
Craig Ringer el

Respuestas:

6

PostgreSQL 11 es compatible INSERT INTO ... ON CONFLICTcon tablas 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


Limitación de particiones ddl

5.10.2.3. Limitaciones

El uso de la cláusula ON CONFLICT con tablas particionadas provocará un error, porque las restricciones únicas o de exclusión solo se pueden crear en particiones individuales. No hay soporte para forzar la unicidad (o una restricción de exclusión) en toda una jerarquía de particionamiento.

ha sido levantado

lad2025
fuente
11

Upsert en tablas particionadas no se implementa en versiones anteriores a Postgres 11.

En Postgres 9.6:

Es poco probable que las declaraciones INSERT con cláusulas ON CONFLICT funcionen como se espera, ya que la acción ON CONFLICT solo se toma en caso de violaciones únicas en la relación objetivo especificada, no en sus relaciones con los hijos.

La partición declarativa no resuelve el problema, Postgres 10:

El uso de la cláusula ON CONFLICT con tablas particionadas provocará un error, porque las restricciones únicas o de exclusión solo se pueden crear en particiones individuales. No hay soporte para forzar la unicidad (o una restricción de exclusión) en toda una jerarquía de particionamiento.

Solución alterna

En Postgres 11 puede usarlo ON CONFLICTen tablas particionadas, vea la respuesta de lad2025.

klin
fuente
Esta respuesta debe actualizarse para pg11, que lo implementa, como señala @ lad2025.
DB140141
@ DB140141 - thx, la respuesta actualizada.
klin