如何使用MySQL中的NULL值进行唯一约束

kab*_*lei 3 mysql null constraints duplicates

我正在寻找如何使用NULL检查实现唯一约束.

MySQL不应该允许多个空值.

雇员:

id | name
---|-----
1  | null
2  | null -> should give error during inserting  2nd row.
Run Code Online (Sandbox Code Playgroud)

Not*_*hus 11

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)
Run Code Online (Sandbox Code Playgroud)

对于软删除,如果只有一行带有每个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
Run Code Online (Sandbox Code Playgroud)

因此,我创建了一个虚拟列,设置后会从 切换1到。nulldeleted_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
Run Code Online (Sandbox Code Playgroud)

添加虚拟列 ,而不是包含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)
Run Code Online (Sandbox Code Playgroud)

不需要插入vconst(但无论如何你不能)。

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

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

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
Run Code Online (Sandbox Code Playgroud)

与设置相同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)
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,根据您软删除、设置的程度deleted_at,您可能希望包含deleted_at到索引中,或者包含一个新索引,但我会让我的负载测试来决定。


Bil*_*win 5

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

https://mariadb.com/kb/en/sql-99/constraint_type-unique-constraint/

UNIQUE约束使得无法执行任何会导致唯一键包含任何非空重复值的操作.(允许多个空值,因为空值永远不会等于任何东西,甚至是另一个空值.)

如果使用UNIQUE约束但不希望多行包含NULL,请将列声明为NOT NULL并禁止任何行具有NULL.

  • 好吧,我的问题是我想在两列上定义一个唯一的键,我只想有一个唯一的组合。这种情况允许我多次使用 (NULL, NULL) ,这对于我的需求来说并不是唯一的。所以这种情况有点棘手。 (2认同)