MySQL ложно разрешает дублировать записи, когда одно из полей - NULL

Используя InnoDB/MySQLi, у меня есть простая таблица: mytable. Таблица имеет четыре поля: id (primary, auto_inc), field1, field2, field3. Все они BIGINT и, кроме id, могут быть NULL.

Я добавил уникальное ограничение:

ALTER TABLE mytable ADD UNIQUE INDEX(field1,field2,field3);

Однако я вполне могу добавить следующие строки без генерации ошибки. Я хотел бы, чтобы это создало ошибку "duplicate", но это не так:

INSERT INTO mytable VALUES (NULL,3,NULL)
INSERT INTO mytable VALUES (NULL,3,NULL)

Он генерирует только "повторяющуюся" ошибку, если все поля имеют значения, отличные от NULL, например,

INSERT INTO mytable VALUES (2,3,4)
INSERT INTO mytable VALUES (2,3,4)

Как я могу сказать MySQL генерировать ошибки "duplicate", даже если у одного (или более) полей есть значения NULL?

EDIT: Это ранее было добавлено как "ошибка" для MySQL: http://bugs.mysql.com/bug.php?id=25544

Ответ 1

Вы не можете сравнить NULL (если вы сравниваете что-либо с NULL, даже NULL = NULL, результаты всегда FALSE), это поведение задокументировано в MySQL ref.

УНИКАЛЬНЫЙ индекс создает ограничение, так что все значения в индексе должны быть разными. Произошла ошибка, если вы попытаетесь добавить новую строку с помощью ключевое значение, которое соответствует существующей строке. Для всех двигателей, UNIQUE index допускает несколько значений NULL для столбцов, которые могут содержать NULL.

Итак, я думаю, что единственный способ - определить столбцы NOT NULL или обработать эту проблему в триггере.

Ответ 2

Корень проблемы - в сравнении с NULL-s. Вы должны понимать логическое значение NULL. И это не "ценность". Не "нулевое значение" или "неизвестное значение", но "нет значения". Это большая разница.

Вот почему создание уникального индекса NULL-возможно - плохая идея. Вы не можете сравнивать NULL, поскольку вы не можете сравнить два значения, оба из которых отсутствуют. Таким образом, СУБД не может поддерживать NULL-s, чтобы быть уникальным, поскольку сравнение не применимо к ним обычным способом. Да, в MySQL (или IS NULL в других СУБД) существуют такие вещи, как <=>, - но это касается технического разрешения того, как для сравнения с значениями NULL, но не с логическими.

Итак, вы находитесь в середине XY-problem. Не используйте NULL-ы с уникальными ключами - они не могут быть там по определению того, что является NULL и что является намерением уникального ключа. И из технической точки зрения (см. Часть о создании индекса), NULL=NULL всегда приведет к ложному - таким образом, ему разрешено вставлять значение NULL если существует другое значение NULL.