如何将具有外键的 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 列添加到已包含数据的现有表中?的主要内容,如果未能解决你的问题,请参考以下文章

使用 CodeIgniter 框架将数据插入到具有外键的多个表中

如何使用具有外键的模型进行 Q 查询?

删除具有外键约束且无级联删除 SQL 的行

如何让jpa休眠创建具有多对一但没有外键的实体

具有该实体外键的学说实体

ef core 如何一次从具有外键的不同表中获取数据? (数据库优先)