场景重现
开发同学为了防止插入多条重复记录,在表中这3个列上加了个唯一性索引: uk_col1_col2_col3 (col1
,col2
,col3
) 。 然而下面这3条数据竟然插入成功了,且没有抛出 ‘Duplicate key’ 的异常:
mysql> insert into uktest(col1,col2,col3) values(1,2,null);
Query OK, 1 row affected (0.01 sec)
mysql> insert into uktest(col1,col2,col3) values(1,2,null);
Query OK, 1 row affected (0.00 sec)
mysql> insert into uktest(col1,col2,col3) values(1,2,null);
Query OK, 1 row affected (0.00 sec)
mysql> select col1,col2,col3 from uktest;
+------+------+------+
| col1 | col2 | col3 |
+------+------+------+
| 1 | 2 | NULL |
| 1 | 2 | NULL |
| 1 | 2 | NULL |
+------+------+------+
3 rows in set (0.00 sec)
上面的三条 SQL 都执行成功了,数据库中插入了多条一样的记录。可按照我们的构想,在执行后两条 SQL 时 应该抛出 ‘Duplicate key’ 的异常的。有点诡异~
查看表结构如下:
mysql> show create table uktest;
+--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| uktest | CREATE TABLE `uktest` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '无意义的ID',
`col1` int(11) NOT NULL DEFAULT '0',
`col2` int(11) NOT NULL DEFAULT '0',
`col3` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_col1_col2_col3` (`col1`,`col2`,`col3`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 |
+--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
col1、col2 都是not null 的,插入时有明确的值。col3 列是 datetime 类型,默认值为 NULL 。
怀疑和 NULL 有关,查查圣经 →_→ 【官方文档】:
A UNIQUE index creates a constraint such that all values in the index must be distinct. An error occurs if you try to add a new row with a key value that matches an existing row. For all engines, a UNIQUE index allows multiple NULL values for columns that can contain NULL.
即不管是采用什么类型的存储引擎,在建立 unique key 的时候都是允许多个 NULL 存在的~~
在 MySQL 中认为 NULL 代表着“未知”。
在 SQL 中,任何值与 NULL 的比较返回值都是 NULL , 而不是 TRUE, 就算 NULL 与 NULL 的比较也是返回 NULL。
何以解忧?
将 col3 列改为 NOT NULL ,且有默认值。时间默认值可以设置为一个有意义的值,比如'1979-01-01 08:00:01' 或 '2000-01-01 00:00:01'。
这也是为啥军规中明确规定:索引字段请务必设置为 not null 的原因之一啦,除了提高索引效率节省空间外,也是为了避免“唯一索引失效了”这类误会啦~
[参考]:唯一性约束与null