如果我用 NULLABLE
列设置唯一键索引,我可以插入任意多行:
create table routes (
id bigint(20) NOT NULL AUTO_INCREMENT,
firstname varchar(255) NOT NULL,
lastname varchar(255) NOT NULL,
departure DATE DEFAULT NULL,
returndate DATE DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uniq (firstname, lastname, departure)
)
为什么?更重要的是:尽管日期列可能为空(并且必须保持 DATE
类型),但我如何才能在日期列上保留唯一约束?
下面的sql语句实际上可以执行多次:
INSERT INTO `routes` (`firstname`, `lastname`, `departure`, `returndate`)
VALUES (NULL, 'john', 'doe', NULL, NULL);
最佳答案
为什么?因为 NULL = NULL
不是真的。
唯一性意味着它不允许另一行在一列中具有相同的值。但在 SQL 中,与任何其他值(包括另一个 NULL)相比,NULL 是“未知的”。三值 bool 逻辑就是这样定义的。
https://dev.mysql.com/doc/refman/8.0/en/create-index.html#create-index-unique说:
A UNIQUE index permits multiple NULL values for columns that can contain NULL.
如果执行两次 INSERT,您期望发生什么?即使“重复”为 NULL,它是否会导致重复键冲突?
您必须创建一个函数索引(在 MySQL 8.0.13 或更高版本上可用):
mysql> CREATE TABLE `routes` (
`id` bigint NOT NULL AUTO_INCREMENT,
`firstname` varchar(255) NOT NULL,
`lastname` varchar(255) NOT NULL,
`departure` date DEFAULT NULL,
`returndate` date DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq` (`firstname`,`lastname`,(coalesce(`departure`, '1900-01-01')))
);
mysql> INSERT INTO `routes` (`id`, `firstname`, `lastname`, `departure`, `returndate`)
-> VALUES (NULL, 'john', 'doe', NULL, NULL);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO `routes` (`id`, `firstname`, `lastname`, `departure`, `returndate`)
-> VALUES (NULL, 'john', 'doe', NULL, NULL);
ERROR 1062 (23000): Duplicate entry 'john-doe-1900-01-01' for key 'uniq'
关于您的评论:不,此功能索引中的 coalesce() 不会影响存储在列中的数据值,它只会影响索引的内容。
mysql> select * from routes;
+----+-----------+----------+-----------+------------+
| id | firstname | lastname | departure | returndate |
+----+-----------+----------+-----------+------------+
| 1 | john | doe | NULL | NULL |
+----+-----------+----------+-----------+------------+
如果你使用 MySQL 5.7+,你可以做类似的事情,但你必须创建一个虚拟列用于索引:
CREATE TABLE `routes` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`firstname` varchar(255) NOT NULL,
`lastname` varchar(255) NOT NULL,
`departure` date DEFAULT NULL,
`departure_notnull` date GENERATED ALWAYS AS (coalesce(`departure`, '1900-01-01')) VIRTUAL,
`returndate` date DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq` (`firstname`,`lastname`,`departure_notnull`)
)
如果您使用 MySQL 5.6 或更早版本,则不能使用虚拟列或功能索引。您必须使该列为 NOT NULL,并使用一个特殊值来表示它是一个非日期。
关于mysql - 如何对多个 NULLABLE 列进行唯一索引约束?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58267590/