【问题标题】:Laravel error in creating a table with two timestamp columnsLaravel 在创建具有两个时间戳列的表时出错
【发布时间】:2019-12-13 15:33:20
【问题描述】:

我在 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: 创建表quarters (quarter_id int unsigned not null, year 整数无符号 非空,quarter_num int 无符号非空,valid_from 时间戳非空,valid_to 时间戳非空,description varchar(255) 空,created_at 时间戳空,updated_at 时间戳 null) 默认字符集 utf8mb4 collat​​e '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 列!

按照 cmets 的要求,我运行了 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`)

【问题讨论】:

  • 需要让它们可以为空:` $table->timestamp('valide_to')->nullable();`
  • 但我不希望它们可以为空。我需要来自用户的价值。是数据库的一些限制吗?另外,如果是这种情况,为什么它接受 valid_from 不为空?
  • 这实际上是一个非常奇怪的场景。一个 nullableTimestamp 似乎以某种方式解决了这个问题,但它是在对抗症状,而不是找到真正问题的解决方案。你能运行php artisan migrate --pretend 并将输出添加到问题中吗?看起来 Laravel 可能会在这里创建错误的 SQL,因为理论上它应该添加一个 CURRENT_TIMESTAMP 作为 SQL 的默认值(参见 github.com/laravel/framework/blob/…
  • @Stratadox 我添加了。此外,我在 PhpMyAdmin 中运行 SQL 语句,然后收到相同的错误:MySQL said: #1067 - Invalid default value for 'valid_to'。这似乎是一个错误的 SQL 或数据库的限制。
  • 你用的是哪个Mysql版本?看起来explicit_defaults_for_timestamp 设置可能会导致这个问题:dev.mysql.com/doc/refman/5.6/en/…

标签: php eloquent mariadb laravel-6


【解决方案1】:

我通过将列的类型从 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 时间戳列。

【讨论】:

  • 这是比时间戳更好的方法
【解决方案2】:

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

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

默认 CURRENT_TIMESTAMP ON 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/

因此,第一个时间戳 (valid_from) 获取自动默认值 CURRENT_TIMESTAMP,这是不可为空的时间戳字段的可接受默认值。第二个字段获得不同的自动默认值,这似乎是数据库无法接受的。

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

在这方面需要注意的重要一点是,您最终选择的解决方案(使用日期时间而不是时间戳)可能是解决问题的更恰当的解决方案:时间戳是一种奇怪的数据类型,主要用作元数据- 仅数据。特别是在 Mysql 和 MariaDB 中,它们固有地遭受"Year 2038 problem" 的困扰,这对于 created_at 或 updated_at 字段再过 18 年左右都不是问题,但当 valid_to 可能在未来几年时可能会出现问题。这只是对不直观的自动默认值的补充......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-26
    • 1970-01-01
    • 1970-01-01
    • 2015-01-13
    • 2021-02-02
    • 1970-01-01
    相关资源
    最近更新 更多