- Error number
- 1452
- SQLSTATE
23000- Symbol
ER_NO_REFERENCED_ROW_2- Product
- MySQL Server
- Versions
- MySQL 5.7, 8.0 and 8.4 (InnoDB tables with FOREIGN KEY constraints)
- Category
- Keys & Constraints
- Last reviewed
- 24 October 2026
On this page
What it means
Error 1452 means an INSERT or UPDATE tried to store a foreign key value in a child table that has no matching row in the parent table, so MySQL rejected it.
A foreign key says that every value in the child column must already exist in the referenced (parent) column. When an INSERT, UPDATE, LOAD DATA or ALTER TABLE would leave a child row pointing at a parent that is not there, InnoDB stops the statement and MySQL returns 1452. The text in brackets tells you everything: the child table (`shop`.`orders`), the constraint name, the child column (customer_id) and the parent table and column (customers (id)). The value itself is not shown, so take it from the statement you ran.
NULL is not checked: a nullable foreign key column can hold NULL without a parent row. Error 1452 is the "child" side of a foreign key; the "parent" side, deleting or changing a parent row that children still use, is error 1451. Both share SQLSTATE 23000 with duplicate key error 1062, so handle the error number, not just the SQLSTATE.
Common causes
- The child row is written before its parent, for example an order inserted before the customer, or an import that loads tables in the wrong order.
- The application sends an id that does not exist: a stale id from a form, an id from another environment, or 0 used to mean "none" instead of NULL.
- An UPDATE changes the foreign key column to a value that has no parent row.
- ALTER TABLE ... ADD FOREIGN KEY on a table that already holds orphan rows (rows whose parent was deleted earlier, often while foreign key checks were switched off).
- The parent row was inserted into a different database or table than the one the constraint references (for example a copy of the table in another schema), or was rolled back.
Diagnose it
Read-only These statements only read metadata (information_schema, performance_schema and SHOW statements) or your own data. They do not change data, privileges or configuration, so they are safe to run first.
Read the constraint: which column points at which parent?
Use the constraint name from the message. For a multi-column key the columns are listed in order.
SELECT table_name, column_name, referenced_table_name, referenced_column_name, ordinal_position
FROM information_schema.key_column_usage
WHERE table_schema = DATABASE()
AND constraint_name = 'fk_orders_customer'
ORDER BY ordinal_position; One row per column: the child column and the parent column it must match.
Check that the parent value exists
SELECT id, name
FROM customers
WHERE id = 99; -- the value your INSERT or UPDATE used No row means the parent really is missing: insert it first or correct the value. If a row comes back but the error still happens, compare the database and table in the message with the ones you queried.
Find orphan rows before adding a foreign key or loading data
An anti-join lists child rows whose value has no parent. Run it against the table (or the staging table) before ALTER TABLE ... ADD FOREIGN KEY.
SELECT oi.order_id, oi.product_id
FROM order_items AS oi
LEFT JOIN products AS p ON p.id = oi.product_id
WHERE p.id IS NULL
AND oi.product_id IS NOT NULL; Every row returned would make the new constraint fail with 1452. An empty result means the constraint can be added.
How to fix it
Each fix is labelled with what it changes. Try read-only checks and the least invasive fix first, and test changes outside production.
Insert the parent row first, in the same transaction
Changes dataWrite parents before children. Wrapping both in one transaction means either both rows are stored or neither is, and the child insert can see the uncommitted parent because it runs in the same session.
START TRANSACTION;
INSERT INTO customers (id, name) VALUES (99, 'Omar');
INSERT INTO orders (id, customer_id) VALUES (101, 99);
COMMIT;
Send a valid id, or NULL when there is no parent
Client or network sideValidate ids in the application before writing, and use NULL (with a nullable column) for "no parent" instead of 0 or an empty string. Keep the foreign key in place: it is the only check that holds when two requests run at once.
Resolve orphan rows, then add the foreign key
Changes dataFor each orphan found by the anti-join, decide with the data owner whether to restore the missing parent, set the child value to NULL (only if the column allows it and the row still makes sense) or remove the child row. Then add the constraint.
Caution: Deleting orphan child rows (DELETE FROM ... WHERE NOT EXISTS) loses data permanently; back them up or copy them to an archive table first. On a large table, ALTER TABLE ... ADD FOREIGN KEY copies or scans the table, so run it in a maintenance window.
-- Option A: restore the missing parent rows
INSERT INTO products (id)
SELECT DISTINCT oi.product_id
FROM order_items AS oi
LEFT JOIN products AS p ON p.id = oi.product_id
WHERE p.id IS NULL AND oi.product_id IS NOT NULL;
-- Then add the constraint
ALTER TABLE order_items
ADD CONSTRAINT fk_items_product FOREIGN KEY (product_id) REFERENCES products (id);
Load imports in dependency order
Changes dataWhen loading several tables, load parents (customers, products) before children (orders, order_items), or load children through a staging table and insert only the rows whose parent exists.
INSERT INTO orders (id, customer_id)
SELECT s.id, s.customer_id
FROM staging_orders AS s
WHERE EXISTS (SELECT 1 FROM customers AS c WHERE c.id = s.customer_id);
Examples
An order for a customer that does not exist
CREATE TABLE customers (
id INT PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT NOT NULL,
CONSTRAINT fk_orders_customer FOREIGN KEY (customer_id) REFERENCES customers (id)
);
INSERT INTO customers VALUES (1, 'Sara');
INSERT INTO orders (id, customer_id) VALUES (101, 99); -- there is no customer 99
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`shop`.`orders`, CONSTRAINT `fk_orders_customer` FOREIGN KEY (`customer_id`) REFERENCES `customers` (`id`))
Reproduced on MySQL 8.0.46, 24 October 2026.
An UPDATE that points a child row at a missing parent
CREATE TABLE customers (id INT PRIMARY KEY, name VARCHAR(100));
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT NOT NULL,
CONSTRAINT fk_orders_customer FOREIGN KEY (customer_id) REFERENCES customers (id)
);
INSERT INTO customers VALUES (1, 'Sara');
INSERT INTO orders VALUES (100, 1);
UPDATE orders SET customer_id = 42 WHERE id = 100;
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`shop`.`orders`, CONSTRAINT `fk_orders_customer` FOREIGN KEY (`customer_id`) REFERENCES `customers` (`id`))
Reproduced on MySQL 8.0.46, 24 October 2026.
A multi-column foreign key where only part of the key matches
CREATE TABLE warehouses (
region CHAR(2),
code INT,
PRIMARY KEY (region, code)
);
CREATE TABLE stock (
id INT PRIMARY KEY,
region CHAR(2),
wh_code INT,
CONSTRAINT fk_stock_wh FOREIGN KEY (region, wh_code) REFERENCES warehouses (region, code)
);
INSERT INTO warehouses VALUES ('UK', 1), ('DE', 3);
INSERT INTO stock VALUES (1, 'UK', 3); -- UK 3 does not exist, only UK 1 and DE 3
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`shop`.`stock`, CONSTRAINT `fk_stock_wh` FOREIGN KEY (`region`, `wh_code`) REFERENCES `warehouses` (`region`, `code`))
Reproduced on MySQL 8.0.46, 24 October 2026.
A foreign key created without a name (MySQL generates one)
CREATE TABLE customers (id INT PRIMARY KEY, name VARCHAR(100));
CREATE TABLE reviews (
id INT PRIMARY KEY,
customer_id INT,
FOREIGN KEY (customer_id) REFERENCES customers (id)
);
INSERT INTO reviews VALUES (1, 5);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`shop`.`reviews`, CONSTRAINT `reviews_ibfk_1` FOREIGN KEY (`customer_id`) REFERENCES `customers` (`id`))
Reproduced on MySQL 8.0.46, 24 October 2026.
How to prevent it
Insert parents before children, inside one transaction when they belong together, and load imports in dependency order. Use NULL, not 0, for an optional relationship. Name your constraints (CONSTRAINT fk_orders_customer ...) so the message points straight at the relationship instead of a generated name such as reviews_ibfk_1.
Check staging data with an anti-join before loading it, and before adding a foreign key to an existing table. Avoid loading data with foreign key checks switched off: MySQL does not re-check existing rows when checks are switched back on, so orphans stay hidden until the next ALTER TABLE or a report that joins the tables.
Common questions
How do I fix MySQL error 1452?
Read the child column and the parent table from the message, then check whether the value you are writing exists in the parent. If it does not, insert the parent row first or correct the value; if the relationship is optional, store NULL instead.
Why do I get 1452 when adding a foreign key to an existing table?
Because the table already contains orphan rows whose value has no parent. Find them with a LEFT JOIN ... WHERE parent.id IS NULL query, restore or correct them, and add the constraint again. The message names a temporary #sql table because ALTER TABLE builds a copy of your table.
Can I just run SET FOREIGN_KEY_CHECKS = 0 to get past error 1452?
Not as a fix. With checks off the orphan rows are stored, and MySQL does not scan the table when checks are switched back on, so the database is left inconsistent without any error. Tools such as mysqldump switch checks off only while restoring data that was consistent when it was dumped.
What is the difference between error 1452 and error 1451?
1452 is raised when a child row would point at a parent that does not exist (INSERT or UPDATE on the child table). 1451 is raised when a parent row that children still reference is deleted or its key is changed.
Learn more on CodeWithSQL
Official documentation
- MySQL 8.0 server error message reference
- FOREIGN KEY constraints
- information_schema KEY_COLUMN_USAGE table
Last technically reviewed 24 October 2026; examples and diagnostics run on MySQL 8.0.46. Message templates checked against the server's error catalogue (perror) for that version.