对于一些背景知识,我们在工作中使用 Zend Framework 2 和 Doctrine 。对于我们自己不填充的值,Doctrine将始终插入NULL
。通常这可以,就好像字段有默认值,然后它应该使用此默认值填充字段。
对于我们运行 MySQL 5.6.16 的其中一个服务器,如下所示的查询运行并执行正常。虽然NULL
被插入到不可为空的字段中,但MySQL会在插入时使用其默认值填充该字段。
在我们运行 MySQL 5.6.20 的另一台服务器上,我们运行下面的查询,但由于它抱怨' field_with_default_value'不能为空。
INSERT INTO table_name(id, field, field_with_default_value)
VALUES(id_value, field_value, NULL);
学说本身并不支持通过" DEFAULT"进入它构建的查询,这不是一个选项。我认为这必须是一个MySQL服务器的东西,看起来好像它在一个版本但不是另一个版本中工作正常,但不幸的是我不知道这可能是什么。我们的SQL模式在两个服务器上都是相同的('NO_AUTO_VALUE_ON_ZERO,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
)。
我应该提一下,如果我实际在Workbench中运行上述SQL,它仍然无法以相同的方式工作。所以它不是一个真正的学说问题,但绝对是某种类型的MySQL问题。
对此的任何帮助将不胜感激。
答案 0 :(得分:1)
根据文档,一切都按预期工作。
测试用例:
mysql> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 5.6.16 |
+-----------+
1 row in set (0.00 sec)
mysql> SELECT @@GLOBAL.sql_mode 'sql_mode::GLOBAL',
@@SESSION.sql_mode 'sql_mode::SESSION';
+------------------------+------------------------+
| sql_mode::GLOBAL | sql_mode::SESSION |
+------------------------+------------------------+
| NO_ENGINE_SUBSTITUTION | NO_ENGINE_SUBSTITUTION |
+------------------------+------------------------+
1 row in set (0.00 sec)
mysql> SET SESSION sql_mode := 'NO_AUTO_VALUE_ON_ZERO,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@GLOBAL.sql_mode 'sql_mode::GLOBAL',
@@SESSION.sql_mode 'sql_mode::SESSION';
+------------------------+-----------------------------------------------------------------------------------------------------------------+
| sql_mode::GLOBAL | sql_mode::SESSION |
+------------------------+-----------------------------------------------------------------------------------------------------------------+
| NO_ENGINE_SUBSTITUTION | NO_AUTO_VALUE_ON_ZERO,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+------------------------+-----------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> SHOW CREATE TABLE `table_name`;
+------------+----------------------------------------------------------------------------+
| Table | Create Table |
+------------+----------------------------------------------------------------------------+
| table_name | CREATE TABLE `table_name` ( |
| | `id` INT(11) UNSIGNED NOT NULL, |
| | `field` VARCHAR(20) DEFAULT NULL, |
| | `field_with_default_value` VARCHAR(20) NOT NULL DEFAULT 'myDefault' |
| | ) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+------------+----------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> INSERT INTO `table_name`(`id`, `field`, `field_with_default_value`)
VALUES
(1, 'Value', NULL);
ERROR 1048 (23000): Column 'field_with_default_value' cannot be null
是否可以发布表格结构的相关部分以了解我们如何提供帮助?
<强>更新强>
MySQL 5.7,使用触发器,可以为问题提供一个可能的解决方案:
<强> Changes in MySQL 5.7.1 (2013-04-23, Milestone 11) 强>
...
- 如果列声明为NOT NULL,则不允许插入 NULL进入列或将其更新为NULL。但是,这种约束 即使有一个BEFORE INSERT(或更新之前)也被强制执行 trigger)将列设置为非NULL值。现在约束 根据SQL标准在语句末尾检查。 (错误
#6295, Bug
#11744964)。...
可能的解决方案:
mysql> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 5.7.4-m14 |
+-----------+
1 row in set (0.00 sec)
mysql> DELIMITER $$
mysql> CREATE TRIGGER `trg_bi_set_default_value` BEFORE INSERT ON `table_name`
FOR EACH ROW
BEGIN
IF (NEW.`field_with_default_value` IS NULL) THEN
SET NEW.`field_with_default_value` :=
(SELECT `COLUMN_DEFAULT`
FROM `information_schema`.`COLUMNS`
WHERE `TABLE_SCHEMA` = DATABASE() AND
`TABLE_NAME` = 'table_name' AND
`COLUMN_NAME` = 'field_with_default_value');
END IF;
END$$
mysql> DELIMITER ;
mysql> INSERT INTO `table_name`(`id`, `field`, `field_with_default_value`)
VALUES
(1, 'Value', NULL);
Query OK, 1 row affected (0.00 sec)
mysql> SELECT `id`, `field`, `field_with_default_value` FROM `table_name`;
+----+-------+--------------------------+
| id | field | field_with_default_value |
+----+-------+--------------------------+
| 1 | Value | myDefault |
+----+-------+--------------------------+
1 row in set (0.00 sec)
答案 1 :(得分:1)
在MySQL升级后,我遇到了同样的问题。原来有一个设置允许对NOT NULL时间戳字段进行NULL插入并获取默认值。
explicit_defaults_for_timestamp=0
答案 2 :(得分:0)
MySQL实际上按预期工作,and that behavior seems to be there to stay。 MariaDB also works the same way now
删除"strict mode"(STRICT_TRANS_TABLES
&amp; STRICT_ALL_TABLES
)应该恢复到之前的行为,但我个人对此没有任何好运(也许我正在做某事错误,但我的@@GLOBAL.sql_mode
和@@SESSION.sql_mode
都不包含严格模式。
我认为这个问题的最佳解决方案是依赖PHP级别的默认值,而不是依靠数据库来提供它们。 There is an existing answer that explains it pretty well.评论也很有帮助。
这样,您还可以获得额外的好处,即模型/实体在实例化时将具有默认值,而不是在数据库中插入时。此外,如果您希望在插入后向用户显示这些值,则无需在SELECT
后执行额外的INSERT
查询。
表示默认值的另一种方法是使用RETURNING
clause,如PostgreSQL中可用,但不是在MySQL中。它可能会在将来的某个时候添加,但现在MariaDB only has it for DELETE
statements。但是,我相信在PHP级别拥有默认值仍然是优越的;即使您从未插入记录,它仍将包含默认值。我从来没有回过头来使用数据库默认值,因为实际上已经实现了这一点。
答案 3 :(得分:0)
根据我的研究,我会说它可能是一个&#34;你&#34;事情和&#34; MySQL&#34;事情。使用SHOW CREATE TABLE table_name;
检查表格定义。记下使用NOT NULL
定义的任何字段。
MySQL 5.6 Reference Manual: 13.2.5 INSERT syntax州:
将NULL插入已声明为NOT NULL的列中。对于 多行INSERT语句或INSERT INTO ... SELECT语句, 该列设置为列数据的 隐式 默认值 类型。数字类型为0,字符串为空字符串(&#39;&#39;) 类型,以及日期和时间类型的“零”值。插入 ... SELECT语句的处理方式与多行插入的处理方式相同 因为服务器不会检查SELECT中的结果集 看它是否返回一行。 (对于单行INSERT,没有 将NULL插入NOT NULL列时发生警告。代替, 该陈述失败并出现错误。)
这意味着您使用的SQL模式无关紧要。如果您正在执行单行 INSERT
(根据您的示例代码)并将NULL
值插入到使用NOT NULL
定义的列中,则不应该工作。
如果您没有在严格的SQL模式下运行,则任何列都不是显式的 给定值设置为其默认值( 显式 或 隐式 )值。对于 例如,如果指定的列列表未指定所有列表 表中的列,未命名的列设置为其默认值。 第11.6节“数据类型”中描述了默认值赋值 默认值”。另请参见第1.7.3.3节“无效的约束” 数据”。
因此,你无法获胜! ;-)开玩笑。要做的是接受MySQL表字段上的NOT NULL
实际意味着在执行单行INSERT
时我不会接受字段的NULL值,而不管SQL模式&#39;
所有这一切,手册中的以下内容也是如此:
对于没有显式DEFAULT的NOT NULL列的数据输入 如果INSERT或REPLACE语句不包含任何值,则该子句 列,或UPDATE语句将列设置为NULL,MySQL处理 根据当时生效的 SQL模式的列:
如果启用了严格SQL模式,则会发生事务错误 表和语句将回滚。对于非交易表, 发生错误,但如果第二行或后续行发生这种情况 多行语句,前面的行将是 插入
如果未启用严格模式,MySQL会将列设置为隐式 列数据类型的默认值。
所以,请记住。在业务逻辑(对象)中设置默认值,让数据层从中获取方向。数据库默认值似乎是一个好主意,但如果它们不存在,你会想念它们吗?如果一棵树落在森林里......
答案 4 :(得分:0)
如果您从语句中删除列(名称和值),则将使用默认值。
一些相关建议:
仅在确实需要时定义默认值,并且仅用于非空列。并在不再需要时删除默认值。 (它们与alter table运行一起派上用场,以设置新列的值,但随后立即运行一个新的(便宜!)alter来删除默认值)
上述“空”与类型有关: -数字列为0, -''用于varchar / varbinary列, -时间戳记为“ 1970-01-01 12:34:56”, -等等
这为应用程序节省了许多往返数据库的行程。如果创建的行是完全可预测的,则应用程序无需在创建后对其进行读取就可以了解其内容。 (假设:没有触发器,没有级联)
对于MySQL,我们仅对那些严格的规则作一些特定的例外:
仅由数据库设置名为mysql_row_foo的列。示例:
mysql_row_created_at timestamp(6) not null default '1970-01-01 12:34:56.000000', mysql_row_updated_at timestamp(6) null default null on update current_timestamp,
欢迎在非空列上使用唯一索引,以防止重复数据。例如,在看起来像(id ++,name)的表lookup.brand中的lookup.brand.name上。
mysql_row_foo列类似于列属性。例如,它们由数据同步工具使用。常规应用程序不读取它们,而是将其应用程序端时间戳存储为时期值。示例:
{valid_until_epoch int unsigned not null default 0, last_seen_epoch_ms bigint not null default 0,