PostgreSQL Upsert не работает на секционированной таблице

9

Есть стол, как это:

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

Эта таблица является мастером, от которого наследуется множество разделов. Разделы выполняются MONTH в поле DATE. Например: раздел для августа-2017 будет agg_201708, а его PK будет pk_agg_201708. Существует обычный триггер BEFORE INSERT для перенаправления вставки в соответствующий раздел.

Дело в том, что я хочу сделать UPSERT в эту таблицу. Часть DO CONFLICT не работает.

Код сначала был такой

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

Но затем я заметил, что ограничение (pk_aggregated) - это ограничение в главной таблице, а не в дочерней таблице, где действительно будет выполняться вставка, из-за триггера.

Я изменил пункт КОНФЛИКТ:

ON CONFLICT (user, type, date)

Какие поля ПК, но это тоже не работает.

Есть идеи, как заставить это работать?

Серги Порта
источник
2
Не думаю, что это из-за ограничений в реализации. Это должно действительно обнаружить это и ОШИБКА. Сообщить об ошибке?
Крейг Рингер
5
См. Также эту ветку списка рассылки postgresql.org/message-id/…
Крейг Рингер

Ответы:

6

PostgreSQL 11 поддерживает INSERT INTO ... ON CONFLICTс секционированными таблицами:

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


Ограничение ddl-разбиения

5.10.2.3. Ограничения

Использование предложения ON CONFLICT с многораздельными таблицами приведет к ошибке, поскольку уникальные или исключающие ограничения могут быть созданы только на отдельных разделах. Нет поддержки для обеспечения уникальности (или ограничения исключения) во всей иерархии секционирования.

был отменен

lad2025
источник
11

Upsert для многораздельных таблиц не реализован в версиях, предшествующих Postgres 11.

В Postgres 9.6:

Операторы INSERT с предложениями ON CONFLICT вряд ли будут работать должным образом, поскольку действие ON CONFLICT выполняется только в случае уникальных нарушений в указанном целевом отношении, а не в его дочерних отношениях.

Декларативное разбиение не решает проблему, Postgres 10:

Использование предложения ON CONFLICT с многораздельными таблицами приведет к ошибке, поскольку уникальные или исключающие ограничения могут быть созданы только на отдельных разделах. Нет поддержки для обеспечения уникальности (или ограничения исключения) во всей иерархии секционирования.

Временное решение

  • создавать уникальные индексы ("user", type, date)для всех дочерних таблиц,
  • создать и использовать функцию для вставки / обновления на основе примера 42.2. Исключения с UPDATE / INSERT описаны в документации.

В Postgres 11 вы можете использовать ON CONFLICTразделенные таблицы, см . Ответ lad2025.

Клину
источник
Этот ответ необходимо обновить для pg11, который его реализует, как отмечает @ lad2025.
DB140141
@ DB140141 - спасибо, ответ обновлен.
Клин