【问题标题】:How to add a NOT NULLABLE column with a foreign key to an existing table that already contains data?如何将具有外键的 NOT NULLABLE 列添加到已包含数据的现有表中?
【发布时间】: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 约束。

标签: php mysql database laravel foreign-keys


【解决方案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】:

      我认为解决此问题的最佳方法是:

      1. 创建一个新的迁移,以便将 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);
        }
      }
      
      1. 现在您必须手动或从管理面板为 project_type_id 设置正确的值。

      2. 现在您的列已准备好设置为外键,因此请继续进行新的迁移以将 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 命令来处理这些额外的工作。

        我过去探索过的其他解决方案是在同一个迁移文件中处理更新,但这会混淆。数据库结构和数据,最重要的是,它假设数据库有数据。使用这种方法,即使它不是完全自动化的,您也可以清楚地分离结构和数据。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-02-27
          • 2021-12-25
          • 1970-01-01
          • 1970-01-01
          • 2013-07-13
          • 2011-09-19
          相关资源
          最近更新 更多