/ / Jak zrobić unikalne ograniczenie działa z wartością NULL w MySQL - mysql, null, duplikaty, ograniczenia

Jak zrobić unikalne ograniczenie działa z wartością NULL w MySQL - mysql, null, duplikaty, ograniczenia

Szukam, jak zaimplementować unikalne ograniczenia z kontrolą NULL.

MySQL nie powinien „zezwalać na wiele wartości null.

Pracownik:

id | name
---|-----
1  | null
2  | null -> should give error during inserting  2nd row.

Odpowiedzi:

4 dla odpowiedzi № 1

Nie, MySQL postępuje właściwie zgodnie ze specyfikacją SQL-99.

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

UNIKALNE Ograniczenie uniemożliwia ZAINTERESOWANIEjakąkolwiek operację spowodowałoby, że unikatowy klucz zawierałby dowolne, niezerowe wartości duplikatów. (Dozwolonych jest wiele wartości null, ponieważ wartość null nigdy nie jest równa do wszystkiego, nawet innej wartości zerowej.)

Jeśli używasz ograniczenia UNIQUE, ale nie chcesz wielu wierszy z NULL, zadeklaruj kolumny jako NOT NULL i zakazać każdy wiersz z NULL.


1 dla odpowiedzi nr 2

MySQL 5.7 nie pozwala na obejście problemu:

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)

Przy miękkich usuwaniach byłoby miło, gdybyś miał tylko jeden wiersz ze znakiem a deleted_at = NULL na ograniczenie.

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

Stworzyłem więc wirtualną kolumnę, która będzie się odwracać 1 do null gdy deleted_at zostaje ustawiony.

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

Zamiast włączać deleted_at do ograniczenia unikalnego dodaj wirtualną kolumnę, 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)

Nie ma potrzeby wstawiania vconst (ale i tak nie możesz).

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

Wstawienie go ponownie powoduje błąd wpisu Duplikat.

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

To samo z ustawieniem delete_at, nie musisz dotykać vconst, odwróci się automatycznie.

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)

Teraz możesz wstawić nowy wiersz z tymi samymi ograniczeniami!

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)

W tym przypadku, w zależności od tego, ile łagodnie usuniesz, ustaw deleted_at, możesz dołączyć deleted_at do indeksu lub z nowym indeksem, ale pozwolę, aby decydowały o tym testy obciążenia.