Cannot add foreign key

Август, 2022

# Проблема

Попытка добавить внешний ключ к таблице приводит к одной из ошибок:

General error: 1215 Cannot add foreign key constraint

General error: 3780 Referencing column 'post_id' and referenced column 'id' in foreign key constraint 'xxx' are incompatible

General error: 1824 Failed to open the referenced table

General error: 3730 Cannot drop table 'xxx' referenced by a foreign key constraint

SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails

Все требования к механизму внешних ключей можно найти в документации
Далее рассмотрим самые популярные причины.

# Разные типы полей

Столбцы должно быть одного типа, а для INTEGER или DECIMAL ещё и одного размера.

General error: 3780 Referencing column 'post_id' and referenced column 'id' in foreign key constraint 'xxx' are incompatible

Обычно внешний ключ ссылается на ID другой таблицы и по умолчанию тип ID: UNSIGNED BIG INTEGER

1Schema::create('comments', function (Blueprint $table) {
2 $table->id();
3 
4 $table->bigInteger('post_id')->unsigned();
5 $table->foreign('post_id')->references('id')->on('posts');
6});
highlight by torchlight.dev

Или начиная с 7 версии более лаконичный синтаксис, который создаёт колонку и ключ:

1Schema::create('comments', function (Blueprint $table) {
2 $table->id();
3 
4 $table->foreignId('post_id')->constrained();
5});
highlight by torchlight.dev

Обрати внимание, что в старых версиях по умолчанию тип ID был UNSIGNED INTEGER

# Порядок миграций

Логично, что прежде чем ссылаться на таблицу она уже должна быть создана, тем не менее это часто упускается из виду и сначала создаётся "дочерняя" таблица.

General error: 1824 Failed to open the referenced table

Не всегда ошибка говорит, что не так и может быть общей: Cannot add foreign key constraint

# Данные в таблице

Таблица уже содержит невалидные значения. Вариант часто встречается, когда внешний ключ добавляется на уже созданную таблицу в которой есть данные.

SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails

Ошибка говорит о том, что нельзя добавить ключ, т.к. данные не консистентные. Проблему необходимо устранить вручную установив правильные значения или NULL

Ещё один вариант происходит при откате миграций:

General error: 3730 Cannot drop table 'xxx' referenced by a foreign key constraint

Причина часто в нарушенном порядке миграций или проблемах в down(). Если ошибка происходит при php artisan migrate:fresh, то необходимо вручную удалять таблицы или данные в них.

# Движок таблиц

Родительская таблица не InnoDB. MyISAM не поддерживает внешние ключи.