使用NULL值创建复合唯一索引的首选方法

时间:2016-03-18 20:37:03

标签: mysql sql indexing

我的数据库中包含以下值:

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-季节4-4-”用于上述。

3 个答案:

答案 0 :(得分: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);

unquer背后的算法完全取决于你。我在这里选了一个short UUID。您可能需要一个很长的UUID。或UUID might not be right for you。基于时间可能有效。您可以将其基于插入行中值的MD5。

答案 1 :(得分: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)享受! :)

您将在field1_id,field2_id,field3_id,field4_id,field5_id上拥有真正唯一索引。并且NULL值只允许一次。无需更改脚本。

答案 2 :(得分:0)

使用https://stackoverflow.com/a/27053371/4321262

中给出的答案
INSERT INTO main_itemmaster (tv_series_id, name, tv_season_number, tv_episode_number) values (97736, 'Season 4', 4, NULL);

这将创建一个存储tv_episode_number的新列,如果tv_episode_number为NULL,则创建0。然后在带有tv_series_id,tv_season_number和名称的唯一索引中使用它。

触发器将设置新列的值,并防止多个相同的包含空值的行在更新后插入或变为相同。

现在,如果多次运行,以下insert语句将抛出重复的条目错误:

use Getopt::Long qw(GetOptionsFromString); 
use Getopt::Long;