Вопрос MySQL - уникальный ключ Не работает правильно, или я не понимаю?

Я пытаюсь создать отношение, в которое может быть включена любая из четырех различных частей, но любая коллекция тех же частей должна обрабатываться как уникальная.

Пример: Учреждение должно иметь назначенную компанию, возможно, иметь назначенное местоположение, рабочую группу и программу. У задания может не быть рабочей группы без места.

Предположим, что у нас есть компании A, B, C; местоположения X, Y, Z; рабочие группы I, J, K и программы 1, 2, 3.

Таким образом, действительные отношения могут включать A - X - я - 1 A - Z - 2 ОТ С C - 3 B - Z - K

Но недействительные отношения будут включать A - K (рабочая группа без местоположения) Y - K - 1 (Нет компании)

Итак, чтобы создать мою таблицу, я создал

companyID INT NOT NULL,
FOREIGN KEY companyKEY (companyID) REFERENCES company (companyID),
locationID INT,
FOREIGN KEY locationKEY (locationID) REFERENCES location (locationID),
workgroupID INT,
FOREIGN KEY workgroupKEY (workgroupID) REFERENCES workgroup (workgroupID),
programID INT,
FOREIGN KEY programKEY (programID) REFERENCES program (programID),
UNIQUE KEY companyLocationWorkgroupProgramKEY (companyID, locationID, workgroupID, programID)

Я полагаю, что это будет обрабатывать все мои отношения, кроме необходимости присвоения, иметь место, если есть рабочая группа (которую я могу с удовольствием делать программно или с помощью триггеров, я думаю)

Однако, когда я проверяю эту схему, она позволяет мне ввести следующее...

INSERT INTO test VALUES (1, null, null, null), (1, null, null, null);

... без жалобы. Я предполагаю, что (1, null, null, null) не равен самому, потому что включены null. Если это так, можно ли каким-либо образом справиться с этим соотношением?

Любая помощь будет оценена!

Ответы

Ответ 1

Это функция (хотя и не то, что я ожидал).

Этот поток предлагает сделать ваш ключ Первичным ключом, чтобы получить ожидаемое поведение:

Это функция - значение NULL является undefined значение, поэтому два NULL значения не совпадают. Может быть мало смущает, но имеет смысл, когда вы думаете об этом.

УНИКАЛЬНЫЙ индекс действительно гарантирует, что значения, отличные от NULL, уникальны; ты мог укажите, что ваша колонка не принимает Значения NULL.

Ответ 2

Единственный способ, с помощью которого я могу справиться с этим без дополнительных триггеров/программирования, - это иметь одно значение "None of Above" в каждой из ссылочных таблиц, чтобы ваш тест выглядел как

INSERT INTO test VALUES (1, NO_LOCATION, NO_WORKGROUP, NO_PROGRAM),
                        (1, NO_LOCATION, NO_WORKGROUP, NO_PROGRAM)

Если идентификаторы NO_* - это правильный тип/длина для столбцов идентификатора. Тогда это провалится, как вы ожидали.

Ответ 3

В MySQL NULL!= NULL или что-то еще. Так что UNIQUE не работает. Вы должны использовать другое значение по умолчанию для пробелов, например, нуль

Ответ 4

Я думаю, важно отметить, что существует правильный способ интерпретации и обработки значений NULL, и поведение, проявляемое OP, является именно тем, что предназначено. Вы можете игнорировать это поведение, и вы можете обрабатывать свой запрос любым способом, который вы хотите, без возражений от меня, но было бы неплохо "принять" ответ, который описывает некоторые формы лучших практик, а не нестандартные личные предпочтения.

Или, если вы не согласны с рекомендацией Best Practice, вы можете просто не принимать ответ.

Это не гонка, чтобы получить ответ, принятый как можно быстрее. Думаю, что обсуждение и сотрудничество также будут частью процесса.

Ответ 5

Я вижу, что это было задано в 2009 году. Однако его часто запрашивают в MySQL: https://bugs.mysql.com/bug.php?id=8173 и https://bugs.mysql.com/bug.php?id. = 17825 например. Люди могут нажать на меня, чтобы попытаться привлечь внимание от MySQL.

Начиная с MySQL 5.7, теперь мы можем использовать следующий обходной путь:

ALTER TABLE test 
ADD generatedLocationID INT AS (ifNull(locationID, 0)) NOT NULL,
ADD generatedWorkgroupID INT AS (ifNull(workgroupID, 0)) NOT NULL,
ADD generatedProgramID INT AS (ifNull(programID, 0)) NOT NULL,
ADD UNIQUE INDEX (companyID, generatedLocationID, generatedWorkgroupID, generatedProgramID);

Сгенерированные столбцы являются виртуальными сгенерированными столбцами, поэтому у них нет места для хранения. Когда пользователь вставляет (или обновляет), уникальный индекс приводит к тому, что значение сгенерированных столбцов генерируется на лету, что является очень быстрой операцией.