如何在数据库迁移中使用外键迁移。 (错误号:150 "Foreign key constraint is incorrectly formed")?
How to use foreign keys migration with database migrations. (errno: 150 "Foreign key constraint is incorrectly formed")?
CodeIgniter 4 有一个方便的迁移和播种器解决方案。不使用外键,一切都运行良好。但是当我使用外键时,我得到“无法添加外键”。
这是因为发生的顺序:
快速示例:
table bar
-------------------
| id | name | fooid |
FOREIGN KEY fooid REFERENCES foo.id
table foo
----------
| id | name|
所以当我现在 运行 php spark migrate
或 php spark migrate:refresh
时无法设置外键,因为 bar
table 已创建,但引用foo
table 还不存在。
从技术上讲,我可以 运行 在迁移后 运行 一个单独的函数,但我喜欢 php spark migrate
的一个命令,一切都完成了。
解决这个问题的正确方法是什么?
这是我创建table创建的代码:
CREATE TABLE `bar` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NULL DEFAULT NULL,
`fooid` int(11) UNSIGNED NOT NULL,
CONSTRAINT `pk_bar` PRIMARY KEY(`id`),
CONSTRAINT `bar_fooid_foreign` FOREIGN KEY(`fooid`) REFERENCES `foo` (`id`) ON DELETE SET NULL,
KEY `fooid` (`fooid`)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_general_ci;
CREATE TABLE `foo` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NULL DEFAULT NULL,
CONSTRAINT `pk_foo` PRIMARY KEY(`id`),
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_general_ci;
附录
我觉得我找到问题的方向了。
从评论中注意到,它是正确的,tables 的创建顺序不正确。这可以通过更改文件名中的时间戳来解决,以便 foo
在 bar
之前创建。但这并不能解决问题,我发现了其他东西:
这是我用来迁移 bar
:
的代码
class Bar extends Migration
{
public function up()
{
$fields = [
'id' => [
'type' => 'int',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'name' => [
'type' => 'varchar',
'constraint' => 255,
'default' => null,
],
'fooid' => [
'type' => 'int',
'constraint' => 11,
'unsigned' => true,
],
];
$this->forge->addField($fields);
$this->forge->addPrimaryKey('id');
$this->forge->addKey('fooid');
$this->forge->addForeignKey('fooid', 'foo', 'id', '', 'SET NULL');
$this->forge->createTable('bar');
}
public function down()
{
$this->forge->dropTable('bar');
}
}
这会生成一个 fooid int(11) UNSIGNED NOT NULL
。问题是,foodid
不能是 null
,但 fk 在删除时将值设置为 null
。
但是...字段 null
的默认值是 'null' => true
,即使我手动添加它,它也不会生成可为 null 的字段。
只需 重命名 您的迁移时间戳前缀,这样 table foo
的创建先于 bar
的创建。例如迁移文件名如下:
app/Database/Migrations/2022-02-16-101819_CreateBarMigration.php
app/Database/Migrations/2022-04-22-101819_CreateFooMigration.php ❌
重命名他们的迁移时间戳前缀,因为引用的 table (foo
) 首先出现。
app/Database/Migrations/2022-04-22-101819_CreateBarMigration.php
app/Database/Migrations/2022-02-16-101819_CreateFooMigration.php ✅
最后,重新运行挂起的迁移。 php spark migrate
.
附录 1
参考您新编辑的问题描述,将 foo
的 table 创建查询移动到 bar
的创建查询之前。即:
CREATE TABLE `foo` ( ...)
CREATE TABLE `bar` ( ...)
附录 2
2022-02-16-101819_CreateFooMigration.php
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateFooMigration extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => '100',
]
]);
$this->forge->addKey('id', true);
$this->forge->createTable('foo');
}
public function down()
{
$this->forge->dropTable('foo');
}
}
2022-04-22-101819_CreateBarMigration.php
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateBarMigration extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => '100',
],
'foo_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addForeignKey('foo_id', 'foo', 'id', 'CASCADE', 'RESTRICT' );
$this->forge->createTable('bar');
}
public function down()
{
$this->forge->dropTable('bar');
}
}
CodeIgniter 4 有一个方便的迁移和播种器解决方案。不使用外键,一切都运行良好。但是当我使用外键时,我得到“无法添加外键”。
这是因为发生的顺序:
快速示例:
table bar
-------------------
| id | name | fooid |
FOREIGN KEY fooid REFERENCES foo.id
table foo
----------
| id | name|
所以当我现在 运行 php spark migrate
或 php spark migrate:refresh
时无法设置外键,因为 bar
table 已创建,但引用foo
table 还不存在。
从技术上讲,我可以 运行 在迁移后 运行 一个单独的函数,但我喜欢 php spark migrate
的一个命令,一切都完成了。
解决这个问题的正确方法是什么?
这是我创建table创建的代码:
CREATE TABLE `bar` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NULL DEFAULT NULL,
`fooid` int(11) UNSIGNED NOT NULL,
CONSTRAINT `pk_bar` PRIMARY KEY(`id`),
CONSTRAINT `bar_fooid_foreign` FOREIGN KEY(`fooid`) REFERENCES `foo` (`id`) ON DELETE SET NULL,
KEY `fooid` (`fooid`)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_general_ci;
CREATE TABLE `foo` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NULL DEFAULT NULL,
CONSTRAINT `pk_foo` PRIMARY KEY(`id`),
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_general_ci;
附录
我觉得我找到问题的方向了。
从评论中注意到,它是正确的,tables 的创建顺序不正确。这可以通过更改文件名中的时间戳来解决,以便 foo
在 bar
之前创建。但这并不能解决问题,我发现了其他东西:
这是我用来迁移 bar
:
class Bar extends Migration
{
public function up()
{
$fields = [
'id' => [
'type' => 'int',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'name' => [
'type' => 'varchar',
'constraint' => 255,
'default' => null,
],
'fooid' => [
'type' => 'int',
'constraint' => 11,
'unsigned' => true,
],
];
$this->forge->addField($fields);
$this->forge->addPrimaryKey('id');
$this->forge->addKey('fooid');
$this->forge->addForeignKey('fooid', 'foo', 'id', '', 'SET NULL');
$this->forge->createTable('bar');
}
public function down()
{
$this->forge->dropTable('bar');
}
}
这会生成一个 fooid int(11) UNSIGNED NOT NULL
。问题是,foodid
不能是 null
,但 fk 在删除时将值设置为 null
。
但是...字段 null
的默认值是 'null' => true
,即使我手动添加它,它也不会生成可为 null 的字段。
只需 重命名 您的迁移时间戳前缀,这样 table foo
的创建先于 bar
的创建。例如迁移文件名如下:
app/Database/Migrations/2022-02-16-101819_CreateBarMigration.php
app/Database/Migrations/2022-04-22-101819_CreateFooMigration.php ❌
重命名他们的迁移时间戳前缀,因为引用的 table (foo
) 首先出现。
app/Database/Migrations/2022-04-22-101819_CreateBarMigration.php
app/Database/Migrations/2022-02-16-101819_CreateFooMigration.php ✅
最后,重新运行挂起的迁移。 php spark migrate
.
附录 1
参考您新编辑的问题描述,将 foo
的 table 创建查询移动到 bar
的创建查询之前。即:
CREATE TABLE `foo` ( ...)
CREATE TABLE `bar` ( ...)
附录 2
2022-02-16-101819_CreateFooMigration.php
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateFooMigration extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => '100',
]
]);
$this->forge->addKey('id', true);
$this->forge->createTable('foo');
}
public function down()
{
$this->forge->dropTable('foo');
}
}
2022-04-22-101819_CreateBarMigration.php
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateBarMigration extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => '100',
],
'foo_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addForeignKey('foo_id', 'foo', 'id', 'CASCADE', 'RESTRICT' );
$this->forge->createTable('bar');
}
public function down()
{
$this->forge->dropTable('bar');
}
}