MySQL 唯一性约束与 NULL 值的处理

964 阅读5分钟

很久之前的一个 bug 了,简单记录下。。。

之前做的一个需求,简化描述下就是接受其他组的 MQ 的消息,然后在数据库里插入一条记录。为了防止他们重复发消息,插入多条重复记录,所以在表中的几个列上加了个唯一性索引。

CREATE UNIQUE INDEX IDX_UN_LOAN_PLAN_APP ON testTable (A, B, C);

这时 A,B,C 三列都是不允许 NULL 值的,唯一性约束也是 work 的。
后来由于需求的变化,修改了以前的唯一性约束,又多加了一列。(至于为什么加就不赘述了)。

ALTER TABLE testTable
DROP INDEX IDX_UN_LOAN_PLAN_APP,
ADD UNIQUE KEY `IDX_UN_LOAN_PLAN_APP` (A, B, C, D);

新加的 D 是类型是 datetime, 允许为 NULL,默认值为 NULL。之所以默认值为 NULL,是考虑到不是所有记录都有这个时间的, 如果强行设置一个 Magic Value (比如’1970-01-01 08:00:00‘)当做默认值,看起来很奇怪。

蓝后。。。就出问题了。加了 D 之后,唯一性约束基本就失效了。

Insert into testTable (A,B,C,D) VALUES (1,2,3,NULL); --- OK
Insert into testTable (A,B,C,D) VALUES (1,2,3,NULL); --- OK
Insert into testTable (A,B,C,D) VALUES (1,2,3,NULL); --- OK

上面的三条 SQL 都是可以执行成功的,数据库中会有多条一样的记录。可按照我们以前的构想,在执行后两条 SQL 时 应该抛出 ‘Duplicate key’ 的异常的。

后来查了一下,才发现其实 MySQL 官方文档上已经明确说了这一点, 唯一性索引是允许多个 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 存在的。。。。

indexindex
细想想,其实也蛮合理,毕竟在 MySQL 中认为 NULL 代表着“未知”。 在 SQL 中,任何值与 NULL 的比较返回值都是 NULL 而不是 TRUE, 就算 NULL 与 NULL 的比较也是返回 NULL。

所以只能 fix 了。。。解决办法也蛮简单粗暴的,直接把线上数据刷了一遍,将“1970-01-01 08:00:00”作为默认值,然后把那列改为不允许为 NULL 的了,咳咳。

wuli 滚滚wuli 滚滚

MySQL 官网上也有蛮多人讨论过这个问题,一部分人认为这是 MySQL 的 bug, 另一部分则认为是一个 feature,附上链接。

MySQL Bugs: #8173: unique index allows duplicates with null values

下一篇 【译】Executor, ExecutorService 和 Executors 间的不同 Please enable JavaScript to view the <a href="//disqus.com/?ref_noscript">comments powered by Disqus.</a>

很久之前的一个 bug 了,简单记录下。。。 之前做的一个需求,简化描述下就是接受其他组的 MQ 的消息,然后在数据库里插入一条记录。为了防止他们重复发消息,插入多条重复记录,所以在表中的几个列上加了个唯一性索引。1CREATE UNIQUE INDEX IDX_UN_LOAN_PLAN_APP O

搁了好久没更博客,再不写要被某人 BS 了,咦。。。。。 原文链接 java.util.concurrent.Executor, java.util.concurrent.ExecutorService, java.util.concurrent. Executors 这三者均是 Java Exec

这篇博客是来自对两篇文章的翻译,原文链接如下。这两篇文章都总结了在使用 Spring 框架时可能造成 NoSuchBeanDefinitionException 的情况,以及应该如何解决。 原文链接java - What is a NoSuchBeanDefinitionException and

这应该是过年假期的最后一篇,如果不是,那你一定看到了假博客。(๑•̀ㅂ•́)و✧ 在消费 RabbitMq 中的 Message 时,常常会出现异常,可能是 Message 本身格式不对,或者由于某些原因无法被处理。我一般都是 catch 异常然后抛个 AmqpRejectAndDontRequeu

新年第一篇~~ 🐣🐥🐤🐔 消息队列算是各个系统间通信比较常见的方式了。我们公司用的是是基于 AMQP 协议的 RabbitMq。在 Spring-AMQP 中比较重要的类就是 Message,因为要发送的消息必须要构造成一个 Message 对象来进行传输。Message 对象包括两部分 B

普通程序员,毕业于东中国正常大学(校友都懂哈),目前从事Java开发,对python和搜索也有所了解。生活大爆炸和Running man脑残粉,因为很喜欢李光洙(外号长颈鹿),所以博客取名为Giraffe’s Home,用来记录学习思考中的一些收获,也许会有错误,也许并不完美,但这不就是成长的过程么

三”观”茅庐(一个做过后端,前端,目前在做移动端的程序猴的博客)默默小屋(性情温顺,主业写代码)James Pan’s Blog(真★大神的博客)Jiaxi’s Home(share various techniques & life)仓鼠君(萌萌哒仓鼠君的blog)脚手架与轮子(😘)oo

欢迎大家留言~ 一起交流,学习,努力,成长吧 🎉🎉🎉~~

《人类简史》作者:尤瓦尔·赫拉利 已看完 《腾讯传》 《哈利波特与死亡圣器》 《哈利波特与混血王子》 《哈利波特与凤凰社》 《哈利波特与火焰杯》 《哈利波特与阿兹卡班囚徒》 《哈利波特与密室》 《哈利波特与魔法石》