如何在 MySQL 中对 NULL 值进行唯一约束

问题描述 投票:0回答:3

我正在寻找如何通过 NULL 检查来实现唯一约束。

MySQL 不应允许多个空值。

员工:

id | name
---|-----
1  | null
2  | null -> should give error during inserting  2nd row.
mysql null duplicates constraints
3个回答
12
投票

MySQL 5.7 确实允许解决方法:

mysql> CREATE TABLE `null_test` (
    ->   `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
    ->   `const` varchar(255) NOT NULL DEFAULT '',
    ->   `deleted_at` datetime DEFAULT NULL,
    ->   PRIMARY KEY (`id`)
    -> ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.03 sec)

使用软删除时,如果每个约束只有一行带有

deleted_at = NULL
,那就太好了。

mysql> ALTER TABLE `null_test` ADD `vconst` int(1) GENERATED ALWAYS AS (((NULL = `deleted_at`) or (NULL <=> `deleted_at`))) VIRTUAL;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

因此,我创建了一个虚拟列,当

1
设置时,它将从
null
翻转到
deleted_at

mysql> ALTER TABLE `null_test` ADD UNIQUE KEY `nullable_index` (`const`,`vconst`);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

不要将

deleted_at
添加到唯一约束中,而是添加虚拟列
vconst

mysql> INSERT INTO `null_test` SET `const` = 'Ghost';
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM `null_test` WHERE `const` = 'Ghost';
+--------+-------+------------+--------+
| id     | const | deleted_at | vconst |
+--------+-------+------------+--------+
| 999901 | Ghost | NULL       |      1 |
+--------+-------+------------+--------+
1 row in set (0.01 sec)

无需插入

vconst
(但无论如何你不能)。

mysql> INSERT INTO `null_test` SET `const` = 'Ghost';
ERROR 1062 (23000): Duplicate entry 'Ghost-1' for key 'nullable_index'

再次插入会引发重复输入错误。

mysql> UPDATE `null_test` SET `deleted_at` = NOW() WHERE `const` = 'Ghost';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

与设置

delete_at
相同,无需触摸
vconst
,它会自动翻转。

mysql> SELECT * FROM `null_test` WHERE `const` = 'Ghost';
+--------+-------+---------------------+--------+
| id     | const | deleted_at          | vconst |
+--------+-------+---------------------+--------+
| 999901 | Ghost | 2017-02-16 22:07:45 |   NULL |
+--------+-------+---------------------+--------+
1 row in set (0.00 sec)

mysql> INSERT INTO `null_test` SET `const` = 'Ghost';
Query OK, 1 row affected (0.00 sec)

现在您可以自由插入具有相同约束的新行!

mysql> SELECT * FROM `null_test` WHERE `const` = 'Ghost';
+--------+-------+---------------------+--------+
| id     | const | deleted_at          | vconst |
+--------+-------+---------------------+--------+
| 999901 | Ghost | 2017-02-16 22:07:45 |   NULL |
| 999903 | Ghost | NULL                |      1 |
+--------+-------+---------------------+--------+
2 rows in set (0.01 sec)

在这种情况下,根据您软删除的数量,设置

deleted_at
,您可能希望将
deleted_at
包含到索引中,或者使用它创建一个新索引,但我会让我的负载测试决定。


9
投票

不,根据 SQL-99 规范,MySQL 正在做正确的事情。

https://sql-99.readthedocs.io/en/latest/chapters/20.html#constraint-type-unique-constraint

一个独特的约束使得不可能提交任何操作 会导致唯一键包含任何非空重复值。 (允许多个空值,因为空值永远不会相等 任何东西,甚至另一个空值。)

如果您使用 UNIQUE 约束,但不希望多行带有 NULL,请将列声明为

NOT NULL
并禁止 any 行具有 NULL。

有多种方法可以使用触发器或生成的列来实现解决方法。


1
投票
alter table yourtable add column `virtual_null` varchar(20)  GENERATED ALWAYS AS (if(isnull(`your_nullable_column`),'null',`your_nullable_column`))) VIRTUAL;

alter table yourtable add constraint unique(virtual_null);

做这个就高兴了,在幕后mysql的null是一个哈希值。因为不可能比较两个空值...

抱歉英语不好,祝你好运

© www.soinside.com 2019 - 2024. All rights reserved.