MySQL:插入错误“不能为空”-- DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP。

5
事实上有很多类似的问题,我无法解决这个问题。我正在使用CodeIgniter框架。当我通过传递PHP对象调用ActiveRecord的insert方法时,它会将所有属性作为其值或null发送。这会导致不能为null错误。
表结构:
CREATE TABLE `scheduledTasks` (
  `id` int(11) NOT NULL,
  `type` varchar(255) COLLATE utf8_bin NOT NULL,
  `createTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `executionTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `ownerUserId` int(11) DEFAULT NULL,
  `subjectUserId` text COLLATE utf8_bin,
  `detail` text COLLATE utf8_bin,
  `isExecuted` int(1) DEFAULT '0'
);
ALTER TABLE `scheduledTasks`
  ADD PRIMARY KEY (`id`);
ALTER TABLE `scheduledTasks`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;

我尝试的查询:

INSERT INTO `scheduledTasks` (`id`, `type`, `createTime`, `executionTime`, `ownerUserId`, `detail`, `isExecuted`)
VALUES (NULL, 'QuoteRequest', NULL, NULL, '926', NULL, NULL)

MySQL版本:

+-------------------------+
| VERSION()               |
+-------------------------+
| 5.7.17-0ubuntu0.16.04.1 |
+-------------------------+

explicit_defaults_for_timestamp | OFF

并且 SQL 模式是

+-------------------------------------------------------------------------------------------------------------------------------------------+
| @@SESSION.sql_mode                                                                                                                        |
+-------------------------------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------------------------------------------------------------------------------------------------------+

如果NULL可以触发DEFAULT_TIMESTAMP,那么NOT NULL就会成为你的问题。否则,尝试不插入任何内容到createTimeexecutionTime,看看是否会触发DEFAULT_TIMESTAMP。这个手册有些棘手。https://dev.mysql.com/doc/refman/5.7/en/timestamp-initialization.html - Anthony Rutledge
手册上说你可以使用 NULL 来设置 TIMESTAMP,但它没有提到设置 DATETIME 的情况。 - Anthony Rutledge
1个回答

4

讨论

MySQL 5.7手册指出...

此外,你可以通过将TIMESTAMP列赋值为NULL来初始化或更新当前日期和时间,除非它已经被定义为允许NULL值的属性。

手册没有说明你可以在DATETIME字段上这样做。最好的方法是在INSERT查询中不提供createTimeexecutionTime字段的任何值。那应该会给你DEFAULT_TIMESTAMP。如果失败了,那就算我尽力了。

INSERT INTO `scheduledTasks` (`id`, `type`, `createTime`, `executionTime`, `ownerUserId`, `detail`, `isExecuted`)
VALUES (NULL, 'QuoteRequest', , , '926', NULL, NULL)

此外,请记住关于MySQL默认值,即使您的DATETIME列具有显式的DEFAULT子句。

对于一个NOT NULL列的数据输入,如果没有显式的DEFAULT子句,如果INSERT或REPLACE语句在中不包含任何值,或者UPDATE语句将该列设置为NULL,则MySQL根据当时生效的SQL模式处理该列:

如果启用了严格SQL模式,则事务表会出现错误并回滚该语句。对于非事务表,会发生错误,但如果这发生在多行语句的第二行或后续行,则前面的行将已插入。

如果未启用严格模式,MySQL将该列设置为列数据类型的隐式默认值

最后,关于MySQL手册中的严格模式

对于STRICT_TRANS_TABLES,MySQL将无效值转换为最接近的有效值,并插入调整后的值。如果缺少值,则MySQL插入列数据类型的隐式默认值。在任何情况下,MySQL都会生成警告而不是错误,并继续处理语句。隐式默认值在第12.7节“数据类型默认值”中描述。

结论

总之,如果您处于严格模式(即STRICT_TRANS_TABLES),并且将DATETIME列设置为NOT NULL DEFAULT CURRENT_TIMESTAMP,然后在INSERT期间提供NULL值,之后出现"不能为NULL"错误时,请下次不要提供DATETIME字段的值。
如果您的框架无法在INSERT期间省略值,并且更改SQL模式无效,则更改表以使用(惊呼)TIMESTAMP可能是使用严格模式并使DEFAULT TIMESTAMP出现的唯一选择。

谢谢您的回复。我已将类型更改为TIMESTAMP,现在它按照我的预期工作。 - user1265083
只需注意您需要使用的日期范围。我相信 TIMESTAMP 从1970年开始,仅适用于2038年之前。 - Anthony Rutledge
谢谢。这个表格是用来安排任务的,所以我觉得现在2038年足够了。 :D - user1265083

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接