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.
- 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
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.
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;
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:
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:
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.
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:
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
- Fix “Violates Foreign Key Constraint” in SQL
A child row points at a parent that is not there, or a parent still has children.
- Fix “Duplicate Key Violates Primary Key Constraint” in SQL
The key you are inserting already exists. Find the existing row before you write.
- Fix “Catalog Error: Table Does Not Exist” in SQL
No table by that name is visible to the query. A typo, a missing schema, or a table that's already gone.