2010-11-02 5 views
26

Этот вопрос требует некоторого гипотетического фона. Рассмотрим таблицу employee с столбцами name, date_of_birth, title, salary, используя MySQL как СУБД. Поскольку, если какое-либо данное лицо имеет то же имя и дату рождения, что и другое лицо, они по определению являются тем же лицом (за исключением удивительных совпадений, когда у нас есть два человека по имени Авраам Линкольн, родившийся 12 февраля 1809 года), мы поставим уникальный ключ на name и date_of_birth, что означает «не хранить одного и того же человека дважды». Теперь рассмотрим эти данные:Уникальный ключ с NULL

id name  date_of_birth title   salary 
1 John Smith 1960-10-02 President  500,000 
2 Jane Doe 1982-05-05 Accountant  80,000 
3 Jim Johnson NULL   Office Manager 40,000 
4 Tim Smith 1899-04-11 Janitor   95,000 

Если теперь я пытаюсь запустить следующее заявление, оно должно и подведет:

INSERT INTO employee (name, date_of_birth, title, salary) 
VALUES ('Tim Smith', '1899-04-11', 'Janitor', '95,000') 

Если я попробовать это, он преуспеет:

INSERT INTO employee (name, title, salary) 
VALUES ('Jim Johnson', 'Office Manager', '40,000') 

А теперь мои данные будут выглядеть следующим образом:

id name  date_of_birth title   salary 
1 John Smith 1960-10-02 President  500,000 
2 Jane Doe 1982-05-05 Accountant  80,000 
3 Jim Johnson NULL   Office Manager 40,000 
4 Tim Smith 1899-04-11 Janitor   95,000 
5 Jim Johnson NULL   Office Manager 40,000 

Это не то, что я хочу, но я не могу сказать, что полностью не согласен с тем, что произошло. Если говорить в терминах математических множеств,

{'Tim Smith', '1899-04-11'} = {'Tim Smith', '1899-04-11'} <-- TRUE 
{'Tim Smith', '1899-04-11'} = {'Jane Doe', '1982-05-05'} <-- FALSE 
{'Tim Smith', '1899-04-11'} = {'Jim Johnson', NULL} <-- UNKNOWN 
{'Jim Johnson', NULL} = {'Jim Johnson', NULL} <-- UNKNOWN 

Я думаю, что MySQL говорит: «Так как я не знаю , что Джим Джонсон с датой NULL рождения не является уже в этой таблице, я добавь его.

Мой вопрос: Как предотвратить дубликаты, хотя date_of_birth не всегда известен? Лучшее, что я до сих пор придумал, это переместить date_of_birth в другую таблицу. Проблема с этим, однако, заключается в том, что я могу получить, скажем, двух кассиров с тем же именем, титулом и зарплатой, разными датами рождения и без возможности хранить их обоих без дубликатов.

+7

Имя и дата рождения НЕ являются уникальными. –

+3

Используйте дату рождения дозорного врача, например. '0000-00-00'. – smilingthax

+2

@Paul Tomblin: Я знаю, что это не так. Разве вы не видите, что это не вопрос? –

ответ

18

Основополагающим свойством уникальным ключом является то, что он должен быть уникальным. Выполнение этого ключа Nullable уничтожает это свойство.

Есть два возможных решения вашей проблемы:

  • Один из способов, неправильный путь, должен был бы использовать некоторую волшебную дату для представления неизвестно. Это просто возвращает вам «проблема» СУБД, но не решает проблему в логическом смысле. Ожидайте проблем с двумя записями «Джон Смит» с неизвестными датами рождения. Являются ли эти парни одним и тем же или являются уникальными людьми? Если вы знаете, что они разные, вы вернулись к той же старой проблеме - ваш уникальный ключ просто не уникален. Даже не думайте о назначении целого ряда волшебных дат для представления «неизвестного» - это действительно путь в ад.

  • Лучше всего создать атрибут EmployeeId в качестве суррогатного ключа. Это всего лишь произвольный идентификатор, который вы назначаете отдельным лицам, которых вы знаете являются уникальными. Этот идентификатор часто является целым числом. Затем создайте таблицу Employee, чтобы связать EmployeeId (уникальный, не нулевой ключ ) с тем, что, по вашему мнению, является зависимым атрибутом, в данном случае Имя и дата рождения (любое из которых может быть равно NULL). Используйте ключ суррогата EmployeeId везде, где вы использовали имя/дату рождения. Это добавляет новую таблицу в вашу систему, но решительно решает проблему неизвестных значений.

+0

«Это просто избавляет вас от проблемы СУБД», но не решает проблему в логическом смысле ». В ТОЧКУ! Спасибо, что сказали это. Я хочу решить проблему дизайна, а не проблему физической реализации. –

+0

«Даже не думайте о назначении целого ряда волшебных дат для представления« неизвестного »- это действительно путь в ад». Спасибо, что вы тоже это сказали. Было неприятно видеть, что эта плохая идея получает так много оборотов. –

6

Я думаю, что MySQL делает это прямо здесь. Некоторые другие базы данных (например, Microsoft SQL Server) рассматривают NULL как значение, которое можно вставить только один раз в столбец UNIQUE, но лично я считаю это странным и неожиданным поведением.

Однако, так как это то, что вы хотите, вы можете использовать некоторые «волшебную» значение вместо NULL, например дату долгое время в прошлом

+2

Я не обязательно согласен с тем, что MySQL правильно справляется. Конечный результат - это не то, что я хочу, однако: я получаю дубликаты, что неприемлемо. И для меня «магическое» значение - всего лишь «поддельный NULL». Не обижайся, но мне трудно погладить, что это правильный способ сделать это. –

+0

Кроме того, это не тот NULL, о котором я забочусь о том, чтобы иметь там дважды. Это «Джим Джонсон». –

+0

НИКОГДА не используйте магические значения. – Rafa

3

Существует другой способ сделать это. Добавление столбца (non-nullable) для представления значения String для столбца date_of_birth. Новое значение столбца будет «" (пустая строка), если date_of_birth имеет значение NULL.

Мы назовем столбец date_of_birth_str и создадим уникального сотрудника с ограничениями (имя, дата_обытия_страницы). Поэтому, когда два повторных вызова имеют одно и то же имя и значение null_of_birth, уникальное ограничение все еще работает.

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

+0

Интересная идея , Я не чувствую себя потрясающе, потому что он включает в себя хранение одних и тех же точных данных дважды, но я согласен, что это сработает. –

+0

Если вы хотите туда попасть, я бы предпочел добавить столбец date_of_birth_is_known с целыми числами 1 или 0 ... и вам все равно придется добавлять «IF» везде, где вы работаете с ним. Мне приходилось иметь дело с таким дизайном в прошлом, и это ужасно, в любом случае - со строкой или с флагом int. Иногда люди обновляют одно, иногда оба - в одном порядке, а иногда и другое. Иногда что-то выходит за пределы транзакции ... Затем вы пишете скрипты для проверки согласованности ... ужасной траты времени. – osa

+0

Добавьте триггер db, чтобы синхронизировать поля. –

5

Ваша проблема с отсутствием дубликатов на основе имени не разрешима, потому что у вас нет естественного ключа. Ввод фальшивой даты для людей, чья дата рождения неизвестна, не решит вашу проблему. Джон Смит, родившийся 1900/01/01, по-прежнему будет отличным человеком, чем Джон Смит, родившийся в 1960/03/09.

Я работаю с данными о имени от крупных и малых организаций каждый день, и я могу заверить вас, что у них есть два разных человека с одинаковым именем все время. Иногда с тем же названием. Дата рождения также не является гарантией уникальности, много Джона Смита, родившегося в ту же дату. Если мы работаем с данными офицеров врачей, у нас часто есть два врача с таким же именем, адресом и номером телефона (комбинации с отцом и сыном)

Ваш лучший выбор - иметь идентификатор сотрудника, если вы вставляете данные сотрудника для идентификации каждый сотрудник однозначно. Затем проверьте уникальное имя в пользовательском интерфейсе, и если есть одно или несколько совпадений, спросите пользователя, если он имел в виду их, и если он говорит «нет», вставьте запись. Затем создайте процесс разорения, чтобы исправить проблемы, если кто-то случайно назначил два идентификатора.

0

Идеальное решение для поддержки функций на основе Великобритании, но это становится более сложным, так как mySQL также необходимо будет поддерживать индексы, основанные на функциях. Это предотвратит необходимость использования «поддельных» значений вместо NULL, а также позволит разработчикам решить, как обрабатывать значения NULL в Великобритании. К сожалению, mySQL в настоящее время не поддерживает такую ​​функциональность, о которой я знаю, поэтому у нас остались обходные пути.

CREATE TABLE employee( 
name CHAR(50) NOT NULL, 
date_of_birth DATE, 
title CHAR(50), 
UNIQUE KEY idx_name_dob (name, IFNULL(date_of_birth,'0000-00-00 00:00:00')) 
); 

(Обратите внимание на использование IFNULL() функции в уникальном ключе определения)

+2

Это дает 'ERROR 1064 (42000): у вас есть ошибка в синтаксисе SQL; проверьте руководство, соответствующее версии вашего сервера MySQL, для правильного синтаксиса для использования рядом с 'date_of_birth,' 0000-00-00 00:00:00 ')))' в строке 1' в MySQL 5.5 – CrackerJack9

0

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

Симметричного, чтобы избежать дубликатов и позволить нуль:

сделать уникальный ключ в качестве первичного ключа

+3

Насколько я могу судить , что делает часть столбца первичного ключа фактически удаляет его способность принимать NULL в качестве значения. – ACJ

0

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

Однако проблема, с которой я столкнулся, заключалась в том, что поле данных не обязательно имело значение. Например, если вы добавили поле «name_of_spouse» в свою таблицу, не обязательно было бы значение для каждой строки таблицы. В этом случае первая точка Bullet NealB («неправильный путь») имеет смысл. В этом случае строка «Нет» должна быть вставлена ​​в столбец name_of_spouse для каждой строки, в которой не было известного супруга.

Ситуация, в которой я столкнулся с этой проблемой, заключалась в написании программы с базой данных для классификации IP-трафика. Целью было создание графика IP-трафика в частной сети. Каждый пакет был помещен в таблицу базы данных с уникальным индексом соединения на основе его источника ip и dest, источника порта и протокола dest, транспортного протокола и протокола приложения. Однако многие пакеты просто не имеют протокола приложения. Например, все TCP-пакеты без протокола приложения должны быть классифицированы вместе и должны занимать одну уникальную запись в индексе соединений. Это связано с тем, что я хочу, чтобы эти пакеты формировали единый край моего графика. В этой ситуации я взял свой собственный совет сверху и сохранил строку «Нет» в поле протокола приложения, чтобы гарантировать, что эти пакеты сформировали уникальную группу.

0

Я рекомендую создать дополнительную колонку таблицы checksum, которая будет содержать хеш md5 name и date_of_birth. Drop уникальный ключ (name, date_of_birth), потому что он не решает проблему. Создайте один уникальный ключ в контрольной сумме.

ALTER TABLE employee 
    ADD COLUMN checksum CHAR(32) NOT NULL; 

UPDATE employee 
SET checksum = MD5(CONCAT(name, IFNULL(date_of_birth, ''))); 

ALTER TABLE employee 
    ADD UNIQUE (checksum); 

Это решение создает небольшую техническую нагрузку, причину для каждого вставленного пар, которые необходимо сгенерировать хеш (то же самое для каждого поискового запроса). Для дальнейших улучшений вы можете добавить триггер, который будет генерировать хэш для вас в каждой вставке:

CREATE TRIGGER before_insert_employee 
BEFORE INSERT ON employee 
FOR EACH ROW 
    IF new.checksum IS NULL THEN 
     SET new.checksum = MD5(CONCAT(new.name, IFNULL(new.date_of_birth, ''))); 
    END IF; 
Смежные вопросы