Skip to content
SQL errors / Fix “No Unique Constraint Matching Given Keys” in SQL
SQL error

Fix “No Unique Constraint Matching Given Keys” in SQL

A foreign key points at a column that isn't a primary key or UNIQUE. Give the parent table a key on that column first.

The exact message
DuckDB
Binder Error: Failed to create foreign key: there is no primary key or unique constraint for referenced table "plant_tags"
PostgreSQL
there is no unique constraint matching given keys for referenced table "plant_tags"
SQL Server
There are no primary or candidate keys in the referenced table 'plant_tags' that match the referencing column list in the foreign key 'FK_product_tags_tag_id'.

Msg 1776.

MySQL
Failed to add the foreign key constraint. Missing index for constraint 'product_tags_ibfk_1' in the referenced table 'plant_tags'

Error 1822.

Oracle
ORA-02270: no matching unique or primary key for this column-list
SQLite
foreign key mismatch - "product_tags" referencing "plant_tags"

Raised when a row is written with foreign keys turned on, not by CREATE TABLE. Checked on SQLite 3.53.

The symptom

A CREATE TABLE or ALTER TABLE with a foreign key fails before any row is written. PostgreSQL says there is no unique constraint matching the given keys, SQL Server says there are no primary or candidate keys in the referenced table, and DuckDB says it failed to create the foreign key.

A foreign key promises that each child row points at exactly one parent row. The database can only keep that promise if the parent column can't hold the same value twice, so it must be a PRIMARY KEY or UNIQUE. Here plant_tags.tag_id is a plain column:

The fix: give the parent a key

The query that fails
drop table if exists product_tags;
create or replace table plant_tags (
  tag_id   integer,
  tag_name varchar
);
create table product_tags (
  product_id integer,
  tag_id     integer references plant_tags (tag_id)
);

Running it raises there is no primary key or unique constraint for referenced table.

The corrected query
drop table if exists product_tags;
create or replace table plant_tags (
  tag_id   integer primary key,
  tag_name varchar
);
create table product_tags (
  product_id integer,
  tag_id     integer references plant_tags (tag_id)
);
insert into plant_tags values (1, 'shade'), (2, 'pet safe');
insert into product_tags values (4, 1), (4, 2);
select * from product_tags;

Open the corrected query in the playground

Both versions start by dropping product_tags: DuckDB won't replace plant_tags while a foreign key from an earlier run still points at it.

When the parent table already holds rows

Rather than rebuilding the parent, add the key to it. DuckDB, PostgreSQL, and MySQL accept ALTER TABLE ... ADD PRIMARY KEY:

Right: add the key, then the foreign key
drop table if exists product_tags;
create or replace table plant_tags (
  tag_id   integer,
  tag_name varchar
);
insert into plant_tags values (1, 'shade'), (2, 'pet safe');
alter table plant_tags add primary key (tag_id);
create table product_tags (
  product_id integer,
  tag_id     integer references plant_tags (tag_id)
);
select 'product_tags created' as result;

Adding the key fails if the column already holds a duplicate. DuckDB says Data contains duplicates on indexed column(s). Find and fix the duplicates with group by tag_id having count(*) > 1 first. A duplicate there means the foreign key couldn't have worked anyway: a child row would point at two parents.

A key on a different column

The key has to be on the column the foreign key names. Pointing at tag_name fails even though tag_id is a primary key, and DuckDB words it differently:

Wrong: tag_name isn't a key
drop table if exists product_tags;
create table product_tags (
  product_id integer,
  tag_name   varchar references plant_tags (tag_name)
);

That is referenced table "plant_tags" does not have a primary key or unique constraint on the columns tag_name. Point at the key, or add UNIQUE to tag_name if names really are unique. The same applies to a composite key: a foreign key must name all of its columns, not the first one.

Foreign keys to the SQLShed datasets

The practice tables load from CSV files with no keys, so references products (product_id) fails with this error. Copy the table and add the key yourself. The CSV loader makes id columns BIGINT, and DuckDB also refuses a foreign key between different types (incompatible types between column "product_id" ("BIGINT") and column "product_id" ("INTEGER")), so match it:

Right: a keyed copy of products
drop table if exists product_tags;
create or replace table product_keys as
select product_id, product_name
from products;
alter table product_keys add primary key (product_id);
create table product_tags (
  product_id bigint references product_keys (product_id),
  tag_id     integer
);
insert into product_tags values (4, 1);
select * from product_tags;

How other engines word it

Engine Message
DuckDB Binder Error: Failed to create foreign key: there is no primary key or unique constraint for referenced table "plant_tags"
PostgreSQL there is no unique constraint matching given keys for referenced table "plant_tags"
SQL Server There are no primary or candidate keys in the referenced table 'plant_tags' that match the referencing column list in the foreign key 'FK_product_tags_tag_id'. Msg 1776.
MySQL Failed to add the foreign key constraint. Missing index for constraint 'product_tags_ibfk_1' in the referenced table 'plant_tags' Error 1822.
Oracle ORA-02270: no matching unique or primary key for this column-list
SQLite foreign key mismatch - "product_tags" referencing "plant_tags" Raised when a row is written with foreign keys turned on, not by CREATE TABLE. Checked on SQLite 3.53.

Related errors

Learn more