PostgreSQL Upsert funktioniert nicht mit partitionierten Tabellen

9

Haben Sie einen Tisch wie diesen:

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

Diese Tabelle ist der Master, von dem viele Partitionen erben. Die Partitionen werden bis MONAT im Feld DATUM ausgeführt. Beispiel: Partition für August 2017 wäre agg_201708 und PK wäre pk_agg_201708. Es gibt den üblichen Trigger VOR EINFÜGEN, um die Einfügung auf die richtige Partition umzuleiten.

Die Sache ist, dass ich ein UPSERT in diese Tabelle einfügen möchte. Der DO CONFLICT-Teil funktioniert nicht.

Der Code war zuerst so

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

Aber dann bemerkte ich, dass die Einschränkung (pk_aggregated) diejenige in der Mastertabelle ist und nicht in der untergeordneten Tabelle, in der die Einfügung aufgrund des Triggers wirklich ausgeführt wird.

Ich habe die Klausel CONFLICT geändert in:

ON CONFLICT (user, type, date)

Welches sind die Felder der PK, aber das funktioniert auch nicht.

Irgendeine Idee, wie das funktioniert?

Sergi Porta
quelle
2
Denken Sie nicht, dass dies auf Einschränkungen bei der Implementierung zurückzuführen ist. Es sollte das und ERROR wirklich erkennen. Melde einen technischen Fehler?
Craig Ringer
5
Siehe auch diesen Mailinglisten-Thread postgresql.org/message-id/…
Craig Ringer

Antworten:

6

PostgreSQL 11 unterstützt INSERT INTO ... ON CONFLICTmit partitionierten Tabellen:

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


Einschränkung ddl-Partitionierung

5.10.2.3. Einschränkungen

Die Verwendung der ON CONFLICT-Klausel mit partitionierten Tabellen führt zu einem Fehler, da eindeutige oder Ausschlussbeschränkungen nur für einzelne Partitionen erstellt werden können. Es gibt keine Unterstützung für die Durchsetzung der Eindeutigkeit (oder einer Ausschlussbeschränkung) in einer gesamten Partitionierungshierarchie.

wurde aufgehoben.

lad2025
quelle
11

Upsert für partitionierte Tabellen ist in früheren Versionen als Postgres 11 nicht implementiert.

In Postgres 9.6:

Es ist unwahrscheinlich, dass INSERT-Anweisungen mit ON CONFLICT-Klauseln wie erwartet funktionieren, da die ON CONFLICT-Aktion nur bei eindeutigen Verstößen gegen die angegebene Zielbeziehung und nicht gegen deren untergeordnete Beziehungen ausgeführt wird.

Durch deklarative Partitionierung wird das Problem nicht behoben. Postgres 10:

Die Verwendung der ON CONFLICT-Klausel mit partitionierten Tabellen führt zu einem Fehler, da eindeutige oder Ausschlussbeschränkungen nur für einzelne Partitionen erstellt werden können. Es gibt keine Unterstützung für die Durchsetzung der Eindeutigkeit (oder einer Ausschlussbeschränkung) in einer gesamten Partitionierungshierarchie.

Problemumgehung

  • Erstellen Sie eindeutige Indizes ("user", type, date)für alle untergeordneten Tabellen.
  • Erstellen und Verwenden einer Funktion zum Einfügen / Aktualisieren basierend auf Beispiel 42.2. Ausnahmen mit UPDATE / INSERT in der Dokumentation beschrieben.

In Postgres 11 können Sie ON CONFLICTfür partitionierte Tabellen verwenden, siehe die Antwort von lad2025.

klin
quelle
Diese Antwort muss für pg11 aktualisiert werden, das sie implementiert, wie @ lad2025 feststellt.
DB140141
@ DB140141 - Danke, die Antwort wurde aktualisiert.
klin