web-dev-qa-db-fra.com

Aucune contrainte unique ou d'exclusion correspondant à ON CONFLICT

Je reçois le message d'erreur suivant lors de l'insertion du type suivant:

Question:

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 *

Erreur:

L'exécution de SQL a échoué (Reason: ERROR: il n'y a pas de contrainte unique ou d'exclusion Correspondant à la spécification ON CONFLICT)

J'ai aussi un INDEX unique:

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

Le fait est que parfois cela fonctionne, mais pas à chaque fois. Je au hasard reçois Cette exception, ce qui est vraiment étrange. Il semble qu’il ne puisse pas accéder à cet index Ou qu’il ne sache pas qu’il existe.

Toute suggestion?

J'utilise PostgreSQL 9.5.5.

Exemple lors de l’exécution du code qui tente de trouver ou de créer un compte:

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)

Dans ce cas, je suis sûr que le compte n'existe pas. De plus, il ne génère jamais l'erreur lorsque la personne a déjà un compte. Le problème est que, dans certains cas, cela fonctionne également s'il n'y a pas encore de compte. La requête est exactement la même. 

11
Tiago Babo

Je n’ai pas eu la chance de jouer avec UPSERT, mais je pense que vous avez un cas de docs :

Notez que cela signifie qu'un index unique non-partiel (un index unique Sans prédicat) sera inféré (et donc utilisé par ON CONFLICT) Si un tel index satisfaisant tous les autres critères est disponible. Si une tentative d'inférence Échoue, une erreur est générée.

1
Vao Tsun

Per les docs ,

Tous les index uniques nom_table qui, sans tenir compte de l'ordre, contiennent exactement les colonnes/expressions spécifiées Conflict_target sont déduits (choisis) en tant qu'index d'arbitre . Si un index_predicate est spécifié, il doit, en tant que condition supplémentaire Pour l'inférence, satisfaire les index d'arbitre.

Les médecins continuent à dire:

[index_predicate est utilisé] pour permettre l'inférence d'index uniques partiels

D'une manière discrète, les docs disent que lors de l'utilisation d'un index partiel et de Upserting avec ON CONFLICT, l'index_predicate doit être spécifié . Ce n'est pas Déduit pour vous. J'ai appris ceci ici , et l'exemple suivant le démontre.

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);

de sorte que nous avons:

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

Sans index_predicate nous obtenons une erreur:

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

Mais si vous incluez plutôt 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;

alors il n'y a pas d'erreur et NE RIEN est honoré.

0
unutbu