2016-03-18 2 views
1

I имеют следующие значения в моей базе данных:Предпочтительный способ создания составного уникального индекса с NULL значениями

enter image description here

Я хотел бы создать уникальный ключ на tv_series_id+name+tv_season_number+tv_episode_number. Однако вышеизложенное не работает для моих целей с использованием обычных:

ALTER TABLE main_itemmaster ADD UNIQUE KEY (tv_series_id, tv_season_number, tv_episode_number, name) 

Из-за нулевых значений. Каким образом можно было бы решить эту проблему на уровне базы данных (а не на стороне запроса)?

И, чтобы уточнить, вышеуказанное не должно быть разрешено, так как «97736-Season 4-4-NULL» повторяется 10 раз. Решение, которое я думал о внедрении в качестве последней меры, состояло бы в том, чтобы сохранить дополнительный столбец строк для уникальности, поэтому «97736-Season 4-4-» для вышеуказанного.

+0

Я смущен.MySQL допускает множественные вхождения значений «NULL» в уникальный индекс (http://dev.mysql.com/doc/refman/5.7/en/create-index.html). Итак, ваш метод должен работать. –

+0

@ GordonLinoff обновляет последнее предложение, чтобы уточнить? – David542

+0

Вам нужно разрешить null? Не можете ли вы установить значение по умолчанию для tv_episode_number (например, 0)? – evsheino

ответ

1

В соответствии с просьбой, мое предложение (непроверенные):

ALTER TABLE main_itemmaster ADD COLUMN uniquer BIGINT NOT NULL; 
DELIMITER $$ 
CREATE TRIGGER add_uniquer BEFORE INSERT ON main_itemmaster 
FOR EACH ROW 
    BEGIN 
    SET NEW.uniquer = (SELECT UUID_SHORT()); 
    END $$ 
DELIMITER ; 
ALTER TABLE main_itemmaster ADD UNIQUE KEY (tv_series_id, tv_season_number, tv_episode_number, name, uniquer); 

Алгоритм за uniquer полностью зависит от вас. Я выбрал здесь short UUID. Возможно, вам понадобится длинный UUID. Или UUID might not be right for you. Работает на основе времени. Вы можете установить его на MD5 значений во вставленной строке.

+0

Как это обеспечивает уникальность больше, чем исходное ограничение? (97736, 4, NULL, 'Season 4', generated_id1) и (97736, 4, NULL, 'Season 4', generated_id2) являются уникальными. – evsheino

+0

Не будет ли это решение предотвращать любую полезность 'UNIQUE'? То есть, не позволит ли «97736-Season 4-4-foo» появляться дважды из-за дополнительной колонки «uniquer»? –

+0

@ RickJames Возможно. OP исключает семантическую информацию о других полях данных и что другая информация может практически дисквалифицировать запрос с этими результатами. Хотя мой ответ строго отвечает на вопрос, я задаюсь вопросом, будет ли другой подход с другим естественным ключом иметь больше смысла. IOW, я дал тактический ответ на возможную стратегическую проблему. – bishop

0

Используя ответ, данный в https://stackoverflow.com/a/27053371/4321262

ALTER TABLE main_itemmaster ADD uniq int UNSIGNED NOT NULL DEFAULT 0; 

ALTER TABLE main_itemmaster ADD UNIQUE KEY (tv_series_id, tv_season_number, name, uniq); 

DELIMITER ;; 
CREATE TRIGGER main_itemmaster_before_insert BEFORE INSERT ON main_itemmaster 
FOR EACH ROW 
BEGIN 
    SET NEW.uniq = IFNULL(NEW.tv_episode_number, 0); 
END;; 

CREATE TRIGGER main_itemmaster_before_update BEFORE UPDATE ON main_itemmaster 
FOR EACH ROW 
BEGIN 
    SET NEW.uniq = IFNULL(NEW.tv_episode_number, 0); 
END;; 
DELIMITER ; 

Это создаст новый столбец, который будет хранить tv_episode_number, или 0, если tv_episode_number является NULL. Затем он используется в уникальном индексе с tv_series_id, tv_season_number и именем.

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

Теперь следующий оператор вставки бросит дубликат ошибки ввода, если запустить несколько раз:

INSERT INTO main_itemmaster (tv_series_id, name, tv_season_number, tv_episode_number) values (97736, 'Season 4', 4, NULL); 
1

Более простое решение (если у вас есть много полей, которые могут быть NULL, и если вы все еще хотите иметь строки уникальных)

1) добавить поле unique_hash

ALTER TABLE `TABLE_NAME_HERE` 
    ADD `unique_hash` BIGINT unsigned NOT NULL DEFAULT '0' COMMENT 'JIRA-ISSUE-ID' AFTER `SOME_EXISTING_FIELD` 
    ; 

2) заполняют им

UPDATE TABLE_NAME_HERE SET unique_hash=cast(conv(substring(md5(
    CONCAT(
     IF(field1_id IS NULL, 'NULL',field1_id),'_', 
     IF(field2_id IS NULL, 'NULL',field2_id),'_', 
     IF(field3_id IS NULL, 'NULL',field3_id),'_', 
     IF(field4_id IS NULL, 'NULL',field4_id),'_', 
     IF(field5_id IS NULL, 'NULL',field5_id),'_' 
    ) 
), 1, 16), 16, 10) as unsigned integer); 

3) добавить уникальный индекс на BIGINT (это будет быстрее, так как сложным уникальным индекс - потому что он будет меньше)

ALTER TABLE `TABLE` ADD UNIQUE (`unique_hash`); 

4) добавить триггер

DELIMITER $$ 
CREATE TRIGGER add_unique_hash BEFORE INSERT ON TABLE_NAME_HERE 
FOR EACH ROW 
    BEGIN 
    SET NEW.unique_hash = (cast(conv(substring(md5(
           CONCAT(
            IF(NEW.field1_id IS NULL, 'NULL',NEW.field1_id),'_', 
            IF(NEW.field2_id IS NULL, 'NULL',NEW.field2_id),'_', 
            IF(NEW.field3_id IS NULL, 'NULL',NEW.field3_id),'_', 
            IF(NEW.field4_id IS NULL, 'NULL',NEW.field4_id),'_', 
            IF(NEW.field5_id IS NULL, 'NULL',NEW.field5_id),'_' 
           ) 
          ), 1, 16), 16, 10) as unsigned integer)); 
    END $$ 
DELIMITER ; 

5) ENJOY ! :)

У вас будет realy уникальный индекс в поле1_id, field2_id, field3_id, field4_id, field5_id. И значения NULL будут разрешены только один раз. Никаких изменений в скриптах не требуется.

Смежные вопросы