简体   繁体   English

如何在 Laravel 迁移中将不可为空的列安全地添加到现有表中?

[英]How to add un-nullable columns safely to an existing table in Laravel migration?

I would like to add an un-nullable column to an existing table with some rows in Laravel migration.我想在 Laravel 迁移中有一些行的现有表中添加一个不可为空的列。 In SQL, I understand such an action should be performed inside a transaction in the order of在 SQL 中,我理解这样的操作应该按照以下顺序在事务内执行

  1. adding a column添加一列
  2. initialising the column初始化列
  3. making it un-nullable使其不可为空

so as to guarantee以保证

  • the initialisation to be performed without disrupting the DB integrity, and在不破坏数据库完整性的情况下执行的初始化,以及
  • ALTER TABLE not to violate the NOT NULL constraint, ALTER TABLE不违反NOT NULL约束,

The following is an example PostgreSQL code (assuming users table has a column old_col ), referring to an answer :以下是 PostgreSQL 代码示例(假设users表有一列old_col ),参考答案

BEGIN TRANSACTION;
  ALTER TABLE users ADD COLUMN new_col integer;
  UPDATE users SET new_col = old_col + 1;
  ALTER TABLE users ALTER COLUMN new_col SET NOT NULL;
COMMIT;

An ordinary Laravel migration file like this would not work.像这样的普通 Laravel 迁移文件是行不通的。

public function up()
{
    Schema::table('users', function($table) {
        $table->integer('new_col');  // ->nullable(false) // later?
    });
}

How can one implement a SQL transaction or its equivalent in Laravel migration?如何在 Laravel 迁移中实现 SQL 事务或等效的事务?

NOTE (edited) :注意(已编辑)
If you want to set up the default value, and if you do not need to (absolutely simultaneously) update the column for the existing rows as a function of some values of each row, then you can simply specify ->default(0) or something like that in the migration file (and avoid all the tricks!).如果要设置默认值,并且不需要(绝对同时)将现有行的列更新为每行某些值的函数,则只需指定->default(0)或迁移文件中的类似内容(并避免所有技巧!)。 My intention of the question was not to set up the default for the column to add.我提出这个问题的目的不是为要添加的列设置默认值。

The solution with three queries:三个查询的解决方案:

DB::transaction(function () {
    Schema::table('users', function (Blueprint $table) {
        $table->integer('id_cloned')->nullable();
    });

    App\Models\User::query()->update([
        'id_cloned' => DB::raw('id + 1'),
        'updated_at' => DB::raw('now()') // if you want to touch the timestamp
    ]);

    Schema::table('users', function (Blueprint $table) {
        $table->integer('id_cloned')->nullable(false)->change();
    });
});

Alternative solution without DB::raw parts, but will generate separate update query for every record:没有 DB::raw 部分的替代解决方案,但将为每条记录生成单独的更新查询:

DB::transaction(function () {
    Schema::table('users', function (Blueprint $table) {
        $table->integer('id_cloned')->nullable();
    });

    foreach (App\Models\User::all() as $row) {
        $row->id_cloned = $row->id + 1;
        $row->save();
    }

    Schema::table('users', function (Blueprint $table) {
        $table->integer('id_cloned')->nullable(false)->change();
    });
});

You need to set default value to whatever you want:您需要将default value设置为您想要的任何default value

public function up()
{
    Schema::table('users', function($table) {
        $table->integer('new_col')->default(0); 
    });
}

you can your code in foreach like $methodName = 'item->get'.$method.'()';您可以在 foreach 中编写代码,例如 $methodName = 'item->get'.$method.'()';

class Item {类项目{

getFoo();... getFoo();...

getBar();... getBar();...

} }

$methods = ['Foo','Bar']; $methods = ['Foo','Bar'];

foreach($methods as $method){ foreach($methods 作为 $method){

$methodName = 'item->get'.$method.'()'; $methodName = 'item->get'.$method.'()';

echo $methodName;回声 $methodName;

} }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM