What it means
A foreign key on another table (the child) points at the table you’re dropping (the parent). Dropping the parent would leave that key pointing at nothing, so SQL Server refuses and drops nothing.
Msg 3726, Level 16, State 1, Server 7732422b7f56, Line 1
Could not drop object 'seo_err_mssql.customers' because it is referenced by a FOREIGN KEY constraint.
The message names the parent but not the foreign key or the child table; the query below finds them.
TRUNCATE TABLE on a parent fails for the same reason with error 4712, even when the child table has
no rows:
Msg 4712, Level 16, State 1, Server 7732422b7f56, Line 1
Cannot truncate table 'seo_err_mssql.customers' because it is being referenced by a FOREIGN KEY constraint.
Common causes
- Rebuilding a table in a migration: drop and re-create
customers, whileordersstill has a foreign key to it. - A teardown script that drops tables parent first.
DROP TABLE customers, ordersfails oncustomers, and then still dropsorders. - Emptying a parent table with
TRUNCATE(4712), say to reload reference data. - A foreign key you didn’t know about, from a table in another schema.
How to fix it
Find the foreign keys that point at the table
SELECT fk.name AS foreign_key,
OBJECT_SCHEMA_NAME(fk.parent_object_id) + N'.' + OBJECT_NAME(fk.parent_object_id) AS referencing_table,
N'ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(fk.parent_object_id)) + N'.'
+ QUOTENAME(OBJECT_NAME(fk.parent_object_id))
+ N' DROP CONSTRAINT ' + QUOTENAME(fk.name) + N';' AS drop_statement
FROM sys.foreign_keys AS fk
WHERE fk.referenced_object_id = OBJECT_ID(N'dbo.customers');
Each row is one child table, with the statement that would remove its foreign key.
Drop the children first
If the child tables are going too, drop them before the parent. In one DROP TABLE, list the
referencing table first:
DROP TABLE dbo.orders, dbo.customers;
Drop the foreign key, then the table
If the child stays, remove its foreign key, drop (or rebuild) the parent, and add the key back once the new parent has its rows. Do it in one transaction so nothing else sees the gap:
BEGIN TRANSACTION;
ALTER TABLE dbo.orders DROP CONSTRAINT FK_orders_customers;
DROP TABLE dbo.customers;
-- create and load the new dbo.customers here
ALTER TABLE dbo.orders WITH CHECK
ADD CONSTRAINT FK_orders_customers FOREIGN KEY (customer_id) REFERENCES dbo.customers (id);
COMMIT;
WITH CHECK checks the existing rows, so the key is trusted again. If some orders now point at
customers that no longer exist, it fails with error 547,
which is the moment to find out.
Empty a parent table without TRUNCATE
DELETE checks the foreign key row by row instead: it works when no child row refers to the rows
you delete, and fails with 547 when one does. It’s logged row by row and fires triggers, so it’s
slower than TRUNCATE on a big table. To truncate anyway, drop the foreign key first and add it back
WITH CHECK afterwards, as above.
Reproduce it
On SQL Server 2022 (16.0.4295.3) with sqlcmd, a scratch schema with customers and an orders
table whose customer_id references it:
DROP TABLE seo_err_mssql.customers;
TRUNCATE TABLE seo_err_mssql.customers;
gave the 3726 and 4712 shown above. The query above listed one foreign key:
foreign_key referencing_table drop_statement
----------- ----------------- --------------
FK_orders_customers seo_err_mssql.orders ALTER TABLE [seo_err_mssql].[orders] DROP CONSTRAINT [FK_orders_customers];
TRUNCATE TABLE failed with 4712 while orders was empty, too. DROP TABLE seo_err_mssql.customers, seo_err_mssql.orders failed on customers with 3726 and then dropped orders (we re-created it).
Inside transactions that were rolled back, DROP TABLE seo_err_mssql.orders, seo_err_mssql.customers
dropped both, and ALTER TABLE … DROP CONSTRAINT FK_orders_customers followed by
DROP TABLE seo_err_mssql.customers worked. SQL Server 2019 and 2025 printed the same messages.
In Inlet
A child table’s definition (its CREATE TABLE with constraints) shows each foreign key and the
table it references. On protected connections, a DROP or TRUNCATE in the query editor asks
before it runs and says why, and dropping or emptying a table from the sidebar asks you to type its
name. When SQL Server refuses, Inlet shows its message with SQL Server error 3726, state 1, severity 16
and links to this page.