PostgreSQL Upsert ne fonctionne pas sur la table partitionnée


9

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?


2
Ne pensez pas que cela sera dû à des limitations de mise en œuvre. Il devrait vraiment détecter cela et ERREUR. Signaler un bug?
Craig Ringer

5
Voir aussi ce fil de liste de diffusion postgresql.org/message-id/…
Craig Ringer

Réponses:


6

PostgreSQL 11 prend INSERT INTO ... ON CONFLICTen charge les 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;

DBFiddle Demo


Limitation du 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é.


11

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.

solution de contournement

  • 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 l' exemple 42.2. Exceptions avec UPDATE / INSERT décrites dans la documentation.

Dans Postgres 11, vous pouvez utiliser ON CONFLICTsur des tables partitionnées, voir la réponse de lad2025.


Cette réponse doit être mise à jour pour pg11, qui la met en œuvre, comme le note @ lad2025.
DB140141

@ DB140141 - thx, la réponse a été mise à jour.
klin
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.