如何将具有外键的 NOT NULLABLE 列添加到已包含数据的现有表中?
Posted
技术标签:
【中文标题】如何将具有外键的 NOT NULLABLE 列添加到已包含数据的现有表中?【英文标题】:How to add a NOT NULLABLE column with a foreign key to an existing table that already contains data? 【发布时间】:2017-12-07 13:49:46 【问题描述】:这个问题的一个例子:
假设您有一个表项目。表项目已经 包含数据。现在您想将 project_type_id 列添加到 表,不能为 NULL 并且具有 project_types 的外键 桌子。
数据库看到project_type_id不能为NULL,所以插入了一个 0 在所有现有行的列中。 0 不存在于 project_types 表,因此外键约束失败。这 表示您不能添加外键。
我考虑在 project_types 表中添加第一个“默认”行并将所有项目链接到该类型。这不是一个好的解决方案,因为用户必须将所有类型从“默认”更改为正确的类型。
你对这个问题有什么解决方案?
【问题讨论】:
FK 的全部意义在于强制完整性。不要搜索解决方法,而是确保数据正确。因此,在添加 FK 之前,请确保该列包含有效数据。 在未添加所有数据时将其设置为可为空。在您的情况下,未知 / null 似乎是一个有效的选项。 我想我会使用@jeroen 建议的方法。在后端,我们将字段设为必填,每次有人对项目进行更改时,他需要选择项目类型,直到所有数据都正确填写。然后我们可以添加 NOT NULL 约束。 【参考方案1】:您应该有一个存在于 DB 中的有效外键,例如:1
然后创建一个新的迁移:
$table->integer('project_type_id')->unsigned()->default(1);
$table->foreignkey('project_type_id')->references('id')->on('project_types');
然后用户必须手动更改它们中的每一个或使用foreach
随机更改它们(随机有效ID)。
【讨论】:
【参考方案2】:我认为这里的一种解决方法是首先添加新列没有外键引用。然后更新该列的值,然后添加外键约束。像这样的:
ALTER TABLE projects ADD COLUMN project_type_id INT;
-- now update with correct values
ALTER TABLE projects ADD CONSTRAINT fk_pt_id FOREIGN KEY (project_type_id)
REFERENCES yourTable(parent_id);
【讨论】:
【参考方案3】:我认为解决此问题的最佳方法是:
创建一个新的迁移,以便将 project_type_id 列添加到项目表中:
php artisan make:migration add_project_type_column_to_projects_table --table=projects
public function up()
Schema::table('projects', function (Blueprint $table)
$table->integer("project_type_id")->unsigned();
);
public function down()
Schema::table('projects', function (Blueprint $table)
$table->dropColumn("project_type_id");
);
它将在每列中添加 0 作为默认值,但没问题,因为它还不是外键。
2.定义Project和ProjectType模型的关系
//Your Project class
class Project extends Model
protected $fillable=["your columns..."];
public function type()
return $this->belongsTo(ProjectType::class);
//Your ProjectType class
class ProjectType extends Model
protected $fillable=["Your columns..."];
public function projects()
return $this->hasMany(Project::class);
现在您必须手动或从管理面板为 project_type_id 设置正确的值。
现在您的列已准备好设置为外键,因此请继续进行新的迁移以将 project_type_id 设置为外键。
php artisan make:migration set_project_type_id_column_as_foreign_key --table=projects
public function up()
Schema::table('projects', function (Blueprint $table)
$table->foreign("project_type_id")
->references("id")
->on("project_types")
->onDelete("cascade")//is up to you
->onUpdate("cascade");// is up to you
);
public function down()
Schema::table('projects', function (Blueprint $table)
$table->dropForeign("projects_project_type_id_foreign"); //This one could be different
);
现在您的表有一个新的(不可为空的)外键,其值正确。
【讨论】:
【参考方案4】:如果您必须部署到不同的环境,您可以为 project_type_id 添加一个默认值(如@Ahmad Mobaraki 建议的那样)。当然,您必须在 project_type 表中具有该值。
然后,根据您的业务逻辑,创建一个 Artisan 命令来获取所有行并将 project_type_id 更新为正确的值。
最后,运行迁移后,运行 Artisan 自定义命令。
此时,您有一个具有正确值的不可为空的外键。
我遇到了同样的问题(是的,我知道,2019 年)并以这种方式解决了。其他环境部署后,运行更新脚本即可。
我用来创建在自动部署之后手动运行的 Artisan 命令。大多数时候,自动部署就很好,但是当有一些这样的任务时,我发现最好的解决方案是使用一个自定义的 Artisan 命令来处理这些额外的工作。
我过去探索过的其他解决方案是在同一个迁移文件中处理更新,但这会混淆。数据库结构和数据,最重要的是,它假设数据库有数据。使用这种方法,即使它不是完全自动化的,您也可以清楚地分离结构和数据。
【讨论】:
以上是关于如何将具有外键的 NOT NULLABLE 列添加到已包含数据的现有表中?的主要内容,如果未能解决你的问题,请参考以下文章