唯一索引未生效,表中有重复数据,why ?

场景重现

开发同学为了防止插入多条重复记录,在表中这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

你可能感兴趣的:(唯一索引未生效,表中有重复数据,why ?)