创建带有两个时间戳列的表时出现Laravel错误

时间:2019-12-13 15:33:20

标签: php eloquent mariadb laravel-6

我在Laravel 6.6中创建了一个具有以下定义的表。

public function up()
{
    Schema::create('quarters', function (Blueprint $table) {
        $table->integer('quarter_id')->unsigned();
        $table->integer('year')->unsigned();
        $table->integer('quarter_num')->unsigned();
        $table->timestamp('valid_from');
        $table->timestamp('valid_to'); // <------ error on this line
        $table->string('description')->nullable();
        $table->timestamps();
        $table->primary('quarter_id');
    });
} 

运行迁移命令时,出现以下错误。

  

Illuminate \ Database \ QueryException:SQLSTATE [42000]:语法错误或   访问冲突:1067“ valid_to”的默认值无效(SQL:   创建表quartersquarter_id int unsigned not null,year   无符号整数       不为空,quarter_num int无符号,不为空,valid_from时间戳记不为空,valid_to时间戳记不为空,description   varchar(255)null,created_at时间戳记,updated_at时间戳记   null)默认字符集utf8mb4整理为'utf8mb4_unicode_ci')

这是Eloquent生成的SQL:

CREATE TABLE `quarters`(
    `quarter_id` INT UNSIGNED NOT NULL,
    `year` INT UNSIGNED NOT NULL,
    `quarter_num` INT UNSIGNED NOT NULL,
    `valid_from` TIMESTAMP NOT NULL,
    `valid_to` TIMESTAMP NOT NULL,
    `description` VARCHAR(255) NULL,
    `created_at` TIMESTAMP NULL,
    `updated_at` TIMESTAMP NULL
) DEFAULT CHARACTER SET utf8mb4 COLLATE 'utf8mb4_unicode_ci'

奇怪的是,如果我注释掉valid_to行,那么它将创建没有错误的表。但是valid_to的定义与valid_from相似,而且100%相似,并且不会为valid_from列引发该错误。实际上,似乎数据库不允许两个timestamp列!

根据评论的要求,我运行了php artisan migrate --pretend,结果如下:

C:\xampp\htdocs\voiceit> php artisan migrate --pretend
CreateQuartersTable: create table `quarters` (`quarter_id` int unsigned not null, `year` int unsigned not null, `quarter_num` int unsigned not null, `valid_from` timestamp not null, `valid_to` timestamp not null, `description` varchar(255) null, `created_at` timestamp null, `updated_at` timestamp null) default character set utf8mb4 collate 'utf8mb4_unicode_ci'
CreateQuartersTable: alter table `quarters` add primary key `quarters_quarter_id_primary`(`quarter_id`)
CreatePeopleDatasTable: create table `people_datas` (`mt_id` bigint unsigned not null, `valid_for` int unsigned not null, `local_personal_id` bigint unsigned not null, `first_name` varchar(255) not null, `last_name` varchar(255) not null, `date_of_birth` date null, `date_of_join` date null, `gender` varchar(1) not null, `location_type` varchar(1) not null, `created_at` timestamp null, `updated_at` timestamp null, `deleted_at` timestamp null) default character set utf8mb4 collate 'utf8mb4_unicode_ci'
CreatePeopleDatasTable: alter table `people_datas` add primary key `people_datas_mt_id_valid_for_primary`(`mt_id`, `valid_for`)
CreatePeopleDatasTable: alter table `people_datas` add constraint `people_datas_valid_for_foreign` foreign key (`valid_for`) references `quarters` (`quarter_id`)
CreatePeopleDatasTable: alter table `people_datas` add constraint `people_datas_gender_foreign` foreign key (`gender`) references `genders` (`id`)
CreatePeopleDatasTable: alter table `people_datas` add constraint `people_datas_location_type_foreign` foreign key (`location_type`) references `location_types` (`id`)
CreatePeopleDatasTable: alter table `people_datas` add index `people_datas_last_name_index`(`last_name`)

2 个答案:

答案 0 :(得分:2)

我通过将列的类型从timestamp更改为dateTime解决了我的问题。因此,按如下所示更改表定义解决了我的问题,因为我需要一个日期和时间:

Schema::create('quarters', function (Blueprint $table) {
            $table->integer('quarter_id')->unsigned();
            $table->integer('year')->unsigned();
            $table->integer('quarter_num')->unsigned();
            $table->dateTime('valid_from');
            $table->dateTime('valid_to');            
            $table->string('description')->nullable();
            $table->timestamps();
            $table->primary('quarter_id');
        });

但是,我仍然很想知道我们如何在一个表中有多个not null时间戳列。

答案 1 :(得分:2)

对于第一个时间戳声明,MySql和/或MariaDB中时间戳默认值的默认行为与后续时间戳不同。

  

对于在特定表中使用TIMESTAMP数据类型的第一列,MariaDB具有特殊的行为。对于特定表中使用TIMESTAMP数据类型的第一列,MariaDB自动为该列分配以下属性:

     

DEFAULT CURRENT_TIMESTAMP      在UPDATE CURRENT_TIMESTAMP

     

这意味着,如果未在INSERT或UPDATE查询中为该列显式分配一个值,那么MariaDB将自动使用当前日期和时间初始化该列的值。

     

通过为列指定DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP子句,还可以为使用TIMESTAMP数据类型的列显式启用INSERT和UPDATE查询的自动初始化。在这些条款中,将接受CURRENT_TIMESTAMP的任何同义词,包括CURRENT_TIMESTAMP(),NOW(),LOCALTIME,LOCALTIME(),LOCALTIMESTAMP和LOCALTIMESTAMP()。

https://mariadb.com/kb/en/library/timestamp/

Ergo,第一个时间戳(valid_from)获得CURRENT_TIMESTAMP的自动默认值,这是非空时间戳字段可接受的默认值。第二个字段获得一个不同的自动默认值,该值似乎对于数据库是不可接受的。

解决当前问题的方法可能是遵循MariaDB的建议,并在第二个时间戳(或同时使用两个时间戳)中使用默认的CURRENT_TIMESTAMP作为默认值。 用Laravel的术语来说,可能类似于$table->timestamp('valid_to')->useCurrent();

在这方面要注意的重要一点是,您最终选择的解决方案(使用datetime而不是timestamp)可能更适合解决该问题:timestamps是某种怪异的数据类型,主要用作元数据-仅数据。尤其是在Mysql和MariaDB中,它们固有地遭受"Year 2038 problem"的困扰,这对于另外18年左右的created_at或updated_at字段来说不是问题,但是当valid_to可能在未来几年内可能会成为问题。这只是不直观的自动默认值的补充。