PostgreSQL-Fehler: no unique or exclusion constraint matching the ON CONFLICT specification
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
INSERT ... ON CONFLICT braucht einen Schiedsrichter: einen Unique- (oder Exclusion-) Constraint, der definiert, was "Konflikt" bedeutet. Das von Ihnen geschriebene Konfliktziel muss genau einem entsprechen — dieselbe Spaltenmenge, derselbe Ausdruck, dasselbe Partial-Prädikat. Keine Übereinstimmung, kein Upsert.
Was dieser Fehler bedeutet
PostgreSQL rät nicht, welchen Constraint Sie meinen. ON CONFLICT (email) erfordert einen Unique-Index auf genau (email):
- Ein nicht-eindeutiger Index auf email zählt nicht.
- Ein Unique-Index auf (tenant_id, email) entspricht keinem Ziel von
(email)— und auch nicht umgekehrt. - Ein Ausdrucks-Index auf
lower(email)entspricht nur dem Ziel(lower(email)). - Ein partieller Unique-Index (
WHERE deleted_at IS NULL) entspricht nur einem Ziel mit derselben WHERE-Klausel.
Häufige Ursachen
- Der Unique-Constraint existiert schlicht nicht — Eindeutigkeit wurde angenommen, nie deklariert.
- Die Ziel-Spaltenmenge weicht vom Index ab (die Tenant-Spalte zu vergessen ist der Klassiker in Multi-Tenant-Schemas).
- Der Index ist auf einem Ausdruck oder partiell, und das Konfliktziel wiederholt ihn nicht.
So diagnostizieren Sie ihn
\d users -- listet Indizes und Constraints mit ihrer genauen Definition
Vergleichen Sie das Konfliktziel im fehlschlagenden INSERT mit den Indexdefinitionen und prüfen Sie Spalten, Ausdrücke und WHERE-Klauseln buchstäblich.
So beheben Sie ihn
-- Der Constraint fehlt → erstellen Sie ihn (in Produktion: CONCURRENTLY)
CREATE UNIQUE INDEX CONCURRENTLY users_email_key ON users (email);
-- Ausdrucks-Index → das Ziel muss ihn wiederholen:
INSERT INTO users (email) VALUES ('[email protected]')
ON CONFLICT (lower(email)) DO NOTHING;
-- Partieller Index → das Ziel muss das Prädikat wiederholen:
INSERT INTO users (email) VALUES ('[email protected]')
ON CONFLICT (email) WHERE deleted_at IS NULL DO NOTHING;
Alternativ benennen Sie den Constraint direkt — ON CONFLICT ON CONSTRAINT users_email_key — obwohl die Spaltenform Umbenennungen des Constraints besser übersteht.
🔍
Der Fehler, den ON CONFLICT verhindern soll: duplicate key value violates unique constraint.
🔍
Weiterführend: Den richtigen Index wählen — Unique-, Partial- und Ausdrucksindizes.
