SQL Server · error reference

SQL Server error 547: FOREIGN KEY constraint conflict

The INSERT statement conflicted with the FOREIGN KEY constraint. The conflict occurred in database "db", table "dbo.parent", column 'id'.

SQL Server's foreign-key violation, and the NOCHECK trap that makes it look solved when it is not — a constraint re-enabled without WITH CHECK is left untrusted.

What the database is telling you

The statement would break a foreign key: either an inserted child has no parent, or a deleted parent still has children. Error 547 covers both directions, and the message names the constraint and column.

Why it shows up in a migration

The standard fix is to switch constraints off for the load with NOCHECK CONSTRAINT ALL and switch them back afterwards. The trap is in the second half. ALTER TABLE ... CHECK CONSTRAINT ALL re-enables the constraint for future writes but does not validate the rows already loaded, and leaves it flagged untrusted — is_not_trusted = 1. The constraint looks enabled in every UI, does not vouch for the data you just inserted, and is quietly excluded from query optimisation. You need WITH CHECK CHECK, which reads like a typo and is not.

Common causes

  • Tables loaded out of dependency order, or in parallel.
  • A foreign key cycle that no ordering can satisfy.
  • Orphaned rows in the source that its own constraints never enforced.
  • Constraints re-enabled without WITH CHECK, so the violation surfaces later on an unrelated write.

How to fix it

  1. 1

    Disable for the load, then re-enable with validation. The doubled CHECK is required.

    EXEC sp_msforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL';
    -- load data --
    EXEC sp_msforeachtable 'ALTER TABLE ? WITH CHECK CHECK CONSTRAINT ALL';
  2. 2

    Audit for constraints left untrusted by an earlier load. On an established database this one is worth running today.

    SELECT OBJECT_NAME(parent_object_id) AS [table], name
      FROM sys.foreign_keys
     WHERE is_not_trusted = 1;
  3. 3

    If re-enabling fails, the loaded data genuinely violates the constraint — find the orphans rather than forcing it.

    SELECT c.* FROM child c
      LEFT JOIN parent p ON p.id = c.parent_id
     WHERE c.parent_id IS NOT NULL AND p.id IS NULL;

What DBShifts does about it

DBShifts re-enables SQL Server constraints with WITH CHECK CHECK, which validates the rows just loaded. Because that validation can legitimately fail, a failure is not left as a log line: it is collected as a migration warning and surfaced to the caller, since the alternative is an unenforced constraint sitting beside a success message.

Bulk transfer engine

Migrating between engines?

DBShifts converts the schema, moves the data, and then proves the two sides match with a type-aware checksum per table — not just a row count. Errors like this one are handled on the way, and the ones that cannot be handled are reported rather than logged and forgotten.

Related errors

← All database errors