首页 > 数据库 >MySQL 中 NULL 导致唯一键失效

MySQL 中 NULL 导致唯一键失效

时间:2022-12-19 17:23:59浏览次数:54  
标签:age work 唯一 索引 MySQL 失效 NULL

最近遇到一个问题,我为一张表中创建了一个唯一键,并且键中字段为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 开发提出过问题。具体可以参考文档,可以看到开发者给出的解释是:

image

感兴趣的童鞋可以点进去看下,关于这个问题是否是一个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

标签:age,work,唯一,索引,MySQL,失效,NULL
From: https://www.cnblogs.com/ghimi/p/mysql-null-uk-invalid.html

相关文章

  • mysql服务器查询慢原因分析方法
    ​​MySQL​​​​数据库​​在查询的时候会出现查询结果很慢,超过1秒,项目中需要找出执行慢的sql进行优化,应该怎么找呢,mysql数据库提供了一个很好的方法,如下: mysql5.0以上......
  • 【mysql-01】开放远程连接权限遇到的问题
    环境电脑本机ip:192.xxx.xxx.xxx虚拟机A的ip:192.168.132.100虚拟机B的ip:192.168.132.101虚拟机A上的mysql用户权限情况:如图,在虚拟机A上的mysql开放了本地电脑ip的......
  • MySQL 8.0 还能为你自动创建主键?!
    MySQL8.0.31版本引入了一个参数​​sql_generate_invisible_primary_key​​​,开启这个参数,会在建表时,检查表中是否有主键,如果没有主键,则会自动创建。该参数非常实用,减少了......
  • 数据库文件导入报错"MySQL server has gone away"
    今天mysql从一个mysql库中导入另一个mariadb库中是总是报:ERROR2006(HY000)atline176infile:'xxx.sql':MySQLserverhasgoneaway但我是在当前服务器上导入的......
  • mysql查询当天所有数据sql语句
    mysql查询当天的所有信息:select*fromtestwhereyear(regdate)=year(now())andmonth(regdate)=month(now())andday(regdate)=day(now())这个有一些繁琐,还有简单的写......
  • MySQL用户管理
    1.查看用户及用户权限mysql中的用户信息和权限等都存储在一个名为mysql的数据库中。其中主要用到的是user、db、tables_priv、columns_priv、procs_priv这五张表,最重要的......
  • docker-compose入门以及部署SpringBoot+Vue+Redis+Mysql(前后端分离项目)以若依前后端
    场景若依前后端分离版手把手教你本地搭建环境并运行项目:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108465662上面在搭建起来前后端分离版的项目后。......
  • MySQL笔记(一)之基础知识
    一、​​mysql​​数据类型​​mysql​​支持的所有的数据类型。1.1.字符串类型​​char(1)​​​是固定长度,我们设置长度是多少,只能输入多少。长度可以再​​0-255​​的......
  • MySQL系列之表和数据类型
    MySQL之表和数据类型一.查看数据库版本selectversion();二.数据库操作2.1.创建数据库createdatabaseifnotexiststest_dbdefaultcharsetutf8collateutf8_gener......
  • docker安装mysql8
    虽然有一种说法是最好不要用docker安装数据库,因为数据库出错后难搞,如果挂载了数据还需要占用双份存储空间等。但有时候还是想搞点新鲜玩意,比如想搞个nextcloud来玩玩,个人感......