Нет единственного ограничения или ограничения исключения, соответствующего ON CONFLICT

Я получаю следующую ошибку при выполнении следующего типа вставки:

Query:

INSERT INTO accounts (type, person_id) VALUES ('PersonAccount', 1) ON
CONFLICT (type, person_id) WHERE type = 'PersonAccount' DO UPDATE SET
updated_at = EXCLUDED.updated_at RETURNING *

Ошибка:

Выполнение SQL не выполнено (Причина: ОШИБКА: нет уникального исключения ограничение, соответствующее спецификации ON CONFLICT)

У меня также есть уникальный INDEX:

CREATE UNIQUE INDEX uniq_person_accounts ON accounts USING btree (type,
person_id) WHERE ((type)::text = 'PersonAccount'::text);

Дело в том, что иногда это работает, но не каждый раз. Я беспорядочно получаю это исключение, которое действительно странно. Кажется, что он не может получить доступ к этому INDEX или он не знает, что он существует.

Любое предложение?

Я использую PostgreSQL 9.5.5.

Пример при выполнении кода, который пытается найти или создать учетную запись:

INSERT INTO accounts (type, person_id, created_at, updated_at) VALUES ('PersonAccount', 69559, '2017-02-03 12:09:27.259', '2017-02-03 12:09:27.259') ON CONFLICT (type, person_id) WHERE type = 'PersonAccount' DO UPDATE SET updated_at = EXCLUDED.updated_at RETURNING *
 SQL execution failed (Reason: ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification)

В этом случае я уверен, что учетной записи не существует. Кроме того, он никогда не выводит ошибку, если у человека уже есть учетная запись. Проблема в том, что в некоторых случаях она также работает, если учетной записи еще нет. Запрос точно такой же.

Ответ 1

У меня не было шанса сыграть с UPSERT, но я думаю, что у вас есть случай из docs:

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

Ответ 2

Согласно документам,

Все уникальные индексы table_name, которые, независимо от порядка, содержат в точности столбцы/выражения, указанные в параметре context_target, выводятся (выбираются) в качестве индексов-арбитров. Если указан index_predicate, он, как дополнительное требование для вывода, должен удовлетворять индексам арбитра.

Документы продолжают говорить,

[index_predicate] используется для разрешения вывода частичных уникальных индексов

В недооцененной форме документы говорят, что при использовании частичного индекса и добавлении с ON CONFLICT необходимо указывать index_predicate. Это не выводится для вас. Я узнал об этом здесь, и следующий пример демонстрирует это.

CREATE TABLE test.accounts (
    id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
    type text,
    person_id int);
CREATE UNIQUE INDEX accounts_note_idx on accounts (type, person_id) WHERE ((type)::text = 'PersonAccount'::text);
INSERT INTO  test.accounts (type, person_id) VALUES ('PersonAccount', 10);

так что имеем:

unutbu=# select * from test.accounts;
+----+---------------+-----------+
| id |     type      | person_id |
+----+---------------+-----------+
|  1 | PersonAccount |        10 |
+----+---------------+-----------+
(1 row)

Без index_predicate мы получаем ошибку:

INSERT INTO  test.accounts (type, person_id) VALUES ('PersonAccount', 10) ON CONFLICT (type, person_id) DO NOTHING;
-- ERROR:  there is no unique or exclusion constraint matching the ON CONFLICT specification

Но если вместо этого вы включаете index_predicate, WHERE ((type)::text = 'PersonAccount'::text):

INSERT INTO  test.accounts (type, person_id) VALUES ('PersonAccount', 10)
ON CONFLICT (type, person_id)
WHERE ((type)::text = 'PersonAccount'::text) DO NOTHING;

тогда нет ошибки, и НИЧЕГО не соблюдается.