MySQL 中 NULL 导致唯一键失效

本文探讨了在MySQL中,NULL值如何影响唯一键约束。当字段包含NULL时,唯一约束可能失效,允许重复记录。解决办法是避免在唯一约束字段上使用NULL,或者将NULL转换为空字符串。同时,文章解释了MySQL中NULL的特殊性质,两个NULL值被视为不相等,因此在唯一索引中可以共存。此外,还介绍了主键索引与唯一索引的区别。
摘要由CSDN通过智能技术生成

最近遇到一个问题,我为一张表中创建了一个唯一键,并且键中字段为NULL,最终导致了唯一约束失效。这里做下分析:

首先新建一张表,包含 work_no,name,age 三个字段:

DROP TABLE IF EXISTS t_emp;
CREATE TABLE t_emp(
  id int(8) not null auto_increment,
  work_no varchar(8) comment  '工号',
  name varchar(255) comment '姓名',
  age int(3) comment '年龄',
  primary key(id),
  unique key(work_no,name,age)
)engine=InnoDB,charset=UTF8mb4,comment="员工表";

从创建语句中我们可以看到,我们为工号,姓名,年龄上添加了唯一约束,现在我们尝试插入数据:

insert into t_emp(work_no,name,age) values ('9527','张三',23);
insert into t_emp(work_no,name,age) values ('9527','张三',23);

我们在尝试插入第二条数据时会报错:

(1062, “Duplicate entry ‘9527-张三-23’ for key ‘work_no’”)

可见唯一键是生效了的,现在我们尝试将 age 为 null 再次尝试下:

insert into t_emp(work_no,name,age) values ('9527','张三',NULL);
insert into t_emp(work_no,name,age) values ('9527','张三',NULL);

可以看到当 age 为空时附加在该字段上的唯一约束失效了。

首先我们可以确认 null 值的出现破坏了唯一性,原本应该被唯一约束限制到的记录被插入到了表中。

如何确保 NULL 不破坏唯一性?

  • 不允许列出现 NULL 值,即NOT NULL
  • 给列增加 DEFUALT 值,注意 DEFAULT 不能是 NULL

所以需要调整建表语句:

DROP TABLE IF EXISTS t_emp;
CREATE TABLE t_emp(
  id int(8) not null auto_increment,
  work_no varchar(8) NOT NULL DEFAULT '' comment  '工号',
  name varchar(255) NOT NULL DEFAULT '' comment '姓名',
  age int(3) NOT NULL DEFAULT 0 comment '年龄',
  primary key(id),
  unique key(work_no,name,age)
)engine=InnoDB,charset=UTF8mb4,comment="员工表";

简单来说就是不要在唯一约束上使用 NULL,但是空字符串是可以的。

如果我们再继续寻找其根本原因的话,就要探究 NULL 在 MySQL 中的实现原理了。

深度探讨

关于NULL 值是否应该被唯一约束限制到的问题,早年有人向 MySQL 开发提出过问题。具体可以参考文档,可以看到开发者给出的解释是:
在这里插入图片描述
感兴趣的童鞋可以点进去看下,关于这个问题是否是一个BUG 的问题探讨的还是很激烈的。最终结果还是显而易见的,MySQL 保留了空值不受唯一约束的限制的这个特性。开发者通过调整空值为空字符串来处理该类异常问题。

NULL 与 NULL 不相等

还记得在 MySQL 中如何以字段值为 NULL 作为条件查询吗?

SELECT * FROM t_emp WHERE age IS NULL;

NULL 在 MySQL 中作为一个特殊的存在,我们无法使用平常使用的等值查询进行查询:

MySQL root@localhost:test> select null = null;
|| null = null ||
| <null> |
1 row in set
Time: 0.001s
MySQL root@localhost:test> SELECT NULL IS NULL;
|| NULL IS NULL ||
| 1 |
1 row in set
Time: 0.001s

比如我们有一个单列的唯一索引,既然实际会有空值的情况,如果唯一约束对空值也有起作用,就会导致仅有一行数据可以为空,这可能会和实际的业务需求相冲突的。所以通常MySQL的存储引擎的唯一索引对NULL值是不适用的。 这也就倒是联合唯一索引的情况下,只要某一列为空,就不会报唯一索引冲突。

或者说,我们一般认为在 MySQL 中 NULL 是一个没有被赋值的值,既然没有被赋值,那么它就有可能被赋值为任意值。这样就可以理解为两个任意值不相等,也就是两个 NULL 值是不相等的,因此也就可以在 唯一索引中单独存在了。

面试题:主键索引与唯一索引的区别

  • 唯一索引的索引列允许空值,而主键索引的列不允许空值
    • 唯一索引列存在空值时,唯一约束对空值时不生效的
  • 主键索引可以被引用为外键,而唯一索引不能
  • 一个表中最多只能创建一个主键索引,单可以创建多个唯一索引

主键索引 = 唯一索引 + 唯一约束 + 非空约束
唯一索引 = 唯一索引 + 唯一约束

参考资料

MySQL: 唯一索引与NULL
Mysql 唯一索引的字段值
Bug #8173 unique index allows duplicates with null values

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值