web-dev-qa-db-fra.com

PostgreSQL Upsert ne fonctionne pas sur la table partitionnée

Ayez une table comme celle-ci:

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

Cette table est le maître dont héritent de nombreuses partitions. Les partitions sont effectuées par MOIS dans le champ DATE. Par exemple: la partition pour août 2017 serait agg_201708 et son PK serait pk_agg_201708 Il y a le déclencheur habituel AVANT INSERT pour rediriger l'insertion vers la partition appropriée.

Le fait est que je veux faire un UPSERT dans ce tableau. La partie DO CONFLICT ne fonctionne pas.

Le code était d'abord comme ça

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

Mais ensuite, j'ai remarqué que la contrainte (pk_aggregated) est celle de la table principale, et non de la table enfant où l'insertion sera réellement effectuée, en raison du déclencheur.

J'ai changé la clause CONFLICT en:

ON CONFLICT (user, type, date)

Quels sont les domaines du PK, mais cela ne fonctionne pas non plus.

Une idée comment faire pour que ça marche?

9
Sergi Porta

PostgreSQL 11 prend en charge INSERT INTO ... ON CONFLICT avec des tables partitionnées:

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;

Démo DBFiddle


Limitation partitionnement ddl

5.10.2.3. Limites

L'utilisation de la clause ON CONFLICT avec des tables partitionnées provoquera une erreur, car des contraintes uniques ou d'exclusion ne peuvent être créées que sur des partitions individuelles. Il n'y a pas de prise en charge pour appliquer l'unicité (ou une contrainte d'exclusion) à travers une hiérarchie de partitionnement entière.

a été levé.

6
lad2025

Upsert sur les tables partitionnées n'est pas implémenté dans les versions antérieures à Postgres 11.

Dans Postgres 9.6:

Il est peu probable que les instructions INSERT avec des clauses ON CONFLICT fonctionnent comme prévu, car l'action ON CONFLICT n'est effectuée qu'en cas de violations uniques sur la relation cible spécifiée, et non sur ses relations enfants.

Le partitionnement déclaratif ne résout pas le problème, Postgres 10:

L'utilisation de la clause ON CONFLICT avec des tables partitionnées provoquera une erreur, car des contraintes uniques ou d'exclusion ne peuvent être créées que sur des partitions individuelles. Il n'y a pas de prise en charge pour appliquer l'unicité (ou une contrainte d'exclusion) à travers une hiérarchie de partitionnement entière.

Workaround

  • créer des index uniques ("user", type, date) sur toutes les tables enfants,
  • créer et utiliser une fonction d'insertion/mise à jour basée sur le Exemple 42.2. Exceptions avec UPDATE/INSERT décrit dans la documentation.

Dans Postgres 11 , vous pouvez utiliser ON CONFLICT sur les tables partitionnées, voir réponse de lad2025.

11
klin