我正在寻找如何使用 NULL 检查实现唯一约束。
MySQL不应该允许多个空值。
员工:
id | name
---|-----
1 | null
2 | null -> should give error during inserting 2nd row.
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
因此,我创建了一个虚拟列,当设置deleted_at
时,该列将从1
翻转到null
。
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
或包含新索引,但我会让我的负载测试决定。
不,根据SQL-99规范,MySQL正在做正确的事情。
https://mariadb.com/kb/en/sql-99/constraint_type-unique-constraint/
UNIQUE 约束使得无法提交任何操作 将导致唯一键包含任何非空重复值。 (允许多个空值,因为空值永远不会相等 到任何内容,甚至是另一个空值。
如果使用 UNIQUE 约束,但不希望多行具有 NULL,请将列声明为 NOT NULL
并禁止任何行具有 NULL。
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是一个哈希值。因为不可能比较两个空值...
对不起,英语不好,祝你好运