Laravel 迁移:"Foreign key constraint is incorrectly formed"(错误号 150)

Laravel migration: "Foreign key constraint is incorrectly formed" (errno 150)

迁移我的数据库时,出现此错误。下面是我的代码,后面是我在尝试 运行 迁移时遇到的错误。

代码

public function up()
{
    Schema::create('meals', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('user_id')->unsigned();
        $table->integer('category_id')->unsigned();
        $table->string('title');
        $table->string('body');
        $table->string('meal_av');
        $table->timestamps();
        $table->foreign('user_id')
            ->references('id')
            ->on('users')
            ->onDelete('cascade');
        $table->foreign('category_id')
            ->references('id')
            ->on('categories')
            ->onDelete('cascade');
    });
}  

错误信息

[Illuminate\Database\QueryException]
SQLSTATE[HY000]: General error: 1005 Can't create table meal.#sql-11d2_1 4 (errno: 150 "Foreign key constraint is incorrectly formed") (SQL: alter
table meals add constraint meals_category_id_foreign foreign key (category_id) references categories (id) on delete cascade)

@JuanBonnett 的 启发了我寻找答案。我使用 Laravel 来自动执行该过程,而不考虑文件本身的创建时间。根据工作流程,“膳食”将在其他 table(类别)之前创建,因为我在类别之前创建了它的架构文件(膳食)。 那是我的错。

在我的例子中,问题是引用的表之一是 InnoDB,另一个是 MyISAM.

MyISAM 不支持外键关系。

所以,现在 两个 表都是 InnoDB。问题已解决。

必须自上而下创建迁移。

首先为 table 不属于任何人的人创建迁移。

然后为属于前一个的 table 创建迁移。


table 引擎问题的简化答案:

要为 table 设置存储引擎,请在架构构建器上设置引擎 属性:

Schema::create('users', function ($table) {
    $table->engine = 'InnoDB';

    $table->increments('id');
});

来自 Laravel 文档:https://laravel.com/docs/5.2/migrations

也许它对登陆这里的任何人都有帮助:我刚刚遇到了同样的问题,在我的情况下,我在外键约束之前的外键列上设置了一个(复合)唯一约束。我通过将 "unique" 语句放在 "foreign" 语句之后解决了这个问题。

作品:

$table->foreign('step_id')->references('id')->on('steps')->onDelete('cascade');
$table->unique(['step_id','lang']);

无效:

$table->unique(['step_id','lang']);
$table->foreign('step_id')->references('id')->on('steps')->onDelete('cascade');

对我来说,一切都按正确的顺序进行,但仍然无法正常工作。后来摸索发现主键必须是无符号的

//this didn't work
$table->integer('id')->unique();
$table->primary('id');

//this worked
$table->integer('id')->unsigned()->unique();
$table->primary('id');

//this worked 
$table->increments('id');

只需在外键末尾添加->unsigned()->index()即可。

如果您在外键定义中使用 ->onDelete('set null'),请确保外键字段本身是 nullable()

//Column definition
$table->integer('user_id')->unsigned()->index()->nullable(); //index() is optional

//...
//...

//Foreign key 
$table->foreign('user_id')
      ->references('id')
      ->on('users')
      ->onDelete('set null');

您应该按顺序创建迁移 例如,我希望我的 users 有一个来自 roles table

role_id 字段

开始做我的角色迁移 php artisan make:migration create_roles_table --create=roles

然后我的第二次用户迁移 php artisan make:migration create_users_table --create=users

php artisan migration 将使用创建文件的顺序执行 2017_08_22_074128_create_roles_table.php 和 2017_08_22_134306_create_users_table 检查日期时间顺序,这将是执行顺序。

个文件 2017_08_22_074128_create_roles_table.php

public function up()
{
    Schema::create('roles', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name', 50);
        $table->timestamps();
    });
}

2017_08_22_134306_create_users_table

public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('role_id')->unsigned();
        $table->string('name');
        $table->string('phone', 20)->unique();
        $table->string('password');
        $table->rememberToken();
        $table->boolean('active');
        $table->timestamps();
        $table->foreign('role_id')->references('id')->on('roles');
    });
}

请在您的字段中添加 ->nullable() 并确保您所指的所有字段都真实存在。

迁移文件的创建顺序应该排序并且外键应该与另一个table中的主键完全相似属性。

在我的例子中,新的 laravel 约定导致了这个错误。

只需简单交换 table 创建 id 就可以了。

$table->increments('id'); // ok

,而不是:

$table->bigIncrements('id'); // was the error.

已经在使用 Laravel v5.8,以前从未出现过此错误。

在 Laravel 中创建新的 table 时。将生成如下迁移:

$table->bigIncrements('id');

而不是(在旧 Laravel 版本中):

$table->increments('id');

当使用 bigIncrements 时,外键需要 bigInteger 而不是 integer。所以您的代码将如下所示:

public function up()
    {
        Schema::create('meals', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedBigInteger('user_id'); //changed this line
            $table->unsignedBigInteger('category_id'); //changed this line
            $table->string('title');
            $table->string('body');
            $table->string('meal_av');
            $table->timestamps();

            $table->foreign('user_id')
                ->references('id')
                ->on('users')
                ->onDelete('cascade');

            $table->foreign('category_id')
                ->references('id')
                ->on('categories')
                ->onDelete('cascade');
        });
    }  

您也可以使用 increments 而不是像 所说的 bigIncrements

Integer 和 BigInteger 的区别在于大小:

  • 整数 => 32 位
  • bigint => 64 位

请记住这一点很重要,引用字段和引用字段必须具有完全相同的数据类型。

我收到了关于数据类型不匹配问题的相同消息。

我将 bigIncrements() 用于 'id',当我将其用作外键时(使用 bigInteger())我得到了错误。

我找到了解决方案,bigIncrements() returns unsignedBigInteger。所以需要在外键

中使用unsignedBigInteger()而不是bigInteger()

分享这个可能对其他人有帮助

创建 "meals"

时应先创建类别和用户 Table

要解决此问题,您应该将类​​别和用户的迁移文件重命名为创建餐前迁移文件的日期 table。


样本:

2019_04_10_050958_create_users_table 
2019_04_10_051958_create_categories_table
2019_04_10_052958_create_meals_table

Laravel 5.8

在外键列中使用 unsignedBigInteger 以避免外键数据类型不匹配的问题。例如,假设我们有两个 tables questionsreplies
问题 table 看起来:

 public function up()
    {
        Schema::create('questions', function (Blueprint $table) {
            $table->bigIncrements('id');
             $table->text('body');
             $table->integer('user_id')->unsigned();
            $table->timestamps();
        });
    }

回复 table 看起来像:

public function up()
{
    Schema::create('replies', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->text('body');
        $table->unsignedBigInteger('question_id');
        $table->integer('user_id')->unsigned();
        $table->foreign('question_id')->references('id')->on('questions')->onDelete('cascade');
        $table->timestamps();
    });
}

很简单的问题,简单回答一下,不要拐弯抹角, 将您的示例 $table->integer('user_id')->unsigned(); 更改为 $table->BigInteger('user_id')->unsigned(); 以解决外键错误。所以在迁移文件中将整数更改为 BigInteger...

解决外键错误的一种方法是禁用检查:"SET FOREIGN_KEY_CHECKS"。 这是一个治标不治本的办法,但真正正确的做法是调整表格及其关系。

   DB::statement('SET FOREIGN_KEY_CHECKS=0;');

   Schema::table('example', function (Blueprint $table) {

        $table->integer('fk_example')->unsigned()->index();
        $table->foreign('fk_example')->references('id')->on('examples');
    });

  DB::statement('SET FOREIGN_KEY_CHECKS=1;');

我只是用 $table->unsignedBigInteger('user_id'); 解决了。 (laravel 5.8)

Laravel 6:2020 年 1 月 17 日更新

$table->bigInteger( 'category_id' )->unsigned();

这对我来说效果很好

  1. 检查你的数据库引用table必须有主键&&自动递增
  2. 删除要迁移的 table,然后 运行 再次迁移

我不得不在 Laravel 6 遇到同样的问题。我通过以下方式解决这个问题。

我认为它对您或其他人有帮助:

    $table->bigIncrements('id');
    $table->bigInteger('user_id')->unsigned(); //chnage this line
    $table->bigInteger('category_id')->unsigned(); //change this line
    ---
    $table->foreign('user_id')
        ->references('id')
        ->on('users')
        ->onDelete('cascade');
    $table->foreign('category_id')
        ->references('id')
        ->on('categories')
        ->onDelete('cascade');

Incrementing ID using a "big integer" equivalent.

used bigInteger instead of Integer

  1. 如果现在还是出错了。

我建议您按照以下方式重新排序您的迁移文件:

Change the dates that form the first part of the migration filenames So they're in the order you want (example: for 2020_07_28_133303_update_categories.php, the date & time is 2020-07-28, 13:33:03);

N.B: First must be 'categories' migration file than 'meals' migration File.

N.B: 在 Laravel 5.6, 对于 $table->increments('id'); 使用 $table->integer('user_id')->unsigned();

我刚刚添加

$table->engine = 'MyISAM';

成功了。 这是因为 laravel 默认使用 InnoDB 引擎创建表。

我遇到了同样的问题,所以我更改了迁移的创建日期,更改了这个,我更改了迁移的执行顺序,并且首先创建了所需的 table table 我用它作为外键

就我而言,问题是 table 引擎之间的差异。在我引用的 table 中,我没有指定引擎。

没用

// Referenced table
Schema::create('budgets', function (Blueprint $table) {
        $table->id();
        $table->timestamps();
        $table->softDeletes();
});

// The other table
    Schema::create('payment', function (Blueprint $table) {
        $table->engine = 'InnoDB';
        $table->integer('budget_id')->unsigned()->nullable();
        $table->foreign('budget_id')
            ->references('id')
            ->on('budgets')
            ->onDelete('cascade');
        $table->timestamps();
});

为了控制它,我建议在所有迁移上设置引擎以创建 tables。 (不要相信默认的数据库设置)

有效

// Referenced table
Schema::create('budgets', function (Blueprint $table) {
        $table->engine = 'InnoDB';
        $table->id();
        $table->timestamps();
        $table->softDeletes();
});

// The other table
    Schema::create('payment', function (Blueprint $table) {
        $table->engine = 'InnoDB';
        $table->integer('budget_id')->unsigned()->nullable();
        $table->foreign('budget_id')
            ->references('id')
            ->on('budgets')
            ->onDelete('cascade');
        $table->timestamps();
});

我正在使用 Laravel 8 并且遇到了同样的错误。问题是这两个列,例如 users.id 和 meals.user_id,其中 user_id 是外键需要相同。

users.id 看起来像这样:

Schema::create('users', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password');
    $table->rememberToken();
    $table->timestamps();
});

在 mySql 中,id 是 Int(10) 未签名的 AUTO ICREMENT。

如果我们转到另一个 table 我们想要设置外键的地方,例如下面的那个我也将 user_id 更改为 unsigned() 。以前我把它简单地写成 $table->integer('user_id') ,这给了我例外,但现在你不会遇到那个错误,因为它们都是 Int(10) 和 Unsigned:

Schema::create('users_permissions', function (Blueprint $table) {
    $table->integer('user_id')->unsigned();
    $table->integer('permission_id')->unsigned();

    //Foreign Key Constraints
    $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
    $table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade');



    //Setting the primary keys
    $table->primary(['user_id','permission_id']);


}); 

您只需要按顺序创建迁移。确保首先创建不接收任何外键的表。然后创建那些做的。 如果您已经创建了迁移,只需更改迁移的时间或日期,这样那些不接收任何外键的表就会先于接收的表创建。