web-dev-qa-db-ja.com

ON CONFLICTに一致する一意または除外の制約はありません

次のタイプの挿入を実行すると、次のエラーが発生します。

クエリ:

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)

この場合、そのアカウントは存在しないはずです。さらに、その人がすでにアカウントを持っている場合はエラーを出力しません。問題は、場合によっては、まだアカウントがない場合にも機能することです。クエリはまったく同じです。

14
Tiago Babo

ドキュメント

順序に関係なく、conflict_targetで指定された列/式を正確に含むすべてのtable_nameユニークインデックスは、アービターインデックスとして推定(選択)されます。 index_predicateが指定されている場合、推論のさらなる要件として、アービターインデックスを満たす必要があります。

ドキュメントは続けて言う、

[index_predicate is u] sedを使用して、部分的に一意のインデックスを推測できます

控えめに言って、ドキュメントは部分的なインデックスを使用して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;
_

その後、エラーは発生せず、何もしないことが優先されます。

7
unutbu

私はUPSERTで遊ぶ機会がなかったが、あなたは docs からのケースを持っていると思う:

これは、他のすべての基準を満たすインデックスが利用可能である場合、非部分一意インデックス(述語のない一意インデックス)が推測される(したがって、ON CONFLICTによって使用される)ことを意味することに注意してください。推論の試みが失敗した場合、エラーが発生します。

2
Vao Tsun