【问题标题】:Using ENUM on unique index for multiple columns在多列的唯一索引上使用 ENUM
【发布时间】:2018-02-12 17:34:39
【问题描述】:

我有一个包含两列的数据库;电子邮件地址和用于标志设置的枚举列。我希望能够在一列(email_addr)上添加唯一索引,但仅测量 ENUM 列是否为值 V(索引 1)。

CREATE TABLE `RegisteredMembers` (
 `reg_id` int(8) NOT NULL AUTO_INCREMENT,
 `mem_name` varchar(70) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Surname',
 `mobile` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
 `email_addr` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL,
 `member_status` enum('V','B','A','P') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'A' 
          COMMENT 'Validated, Blocked, Anonymous, Pending',

 UNIQUE KEY `unique_RegMemId` (`Reg_id`),
 KEY `EmailAddr` (`email_addr`) USING BTREE
) 

ENGINE=MyISAM AUTO_INCREMENT=2292 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

例如,member_status = 'P' 的行可以有重复的 email_addr 值,但member_status = 'V' 的行不能是重复的 email_addr 值。

我已尝试在 PhpMyAdmin 上进行上述操作,但由于当前数据重复(对于非 V ENUM 值),它会返回各种一致性错误或失败。

我有read some posts,它似乎非常广泛地显示this can't be done,但也有可能this might


我知道这里的一些更了解 DB 的人会问 “你疯了吗?你为什么要使用 ENUM?” 我只能说我们使用 ENUM 作为从一个(非常!)旧的 Y/N 标志系统到一个在同一列中有更多选项的系统(从而最大限度地减少前端 PHP 列引用更新的大量注释)。

我/我们可以轻松地将member_status ENUM 列重建为一个 int 值,以引用另一个表,例如 状态表;但是同样的查询又回来了:

我想根据另一列的值将一列索引为唯一

所以当table.member_status == 'V' 那么email_addr 必须是唯一的。

将索引列组合为单个唯一列不起作用,就像 table.member_status == 'P' 然后可以允许 email_addr 列的重复。


例子:

`reg_id`  |  `member_status`  |   `email_addr`
----------------------------------------------------------------
    1     |          P        |  me@homere.com
    2     |          A        |  horse@shoes.com        
    3     |          V        |  martin@koolaide.co.uk
    4     |          A        |  me@homere.com           // this is ok.       
    5     |          A        |  horse@shoes.com         // this is ok too
    6     |          B        |  failed@life.org
    7     |          V        |  martin@koolaide.co.uk   // this is invalid. 
  • 我可以用 ENUM 做到这一点吗?
  • 如果不能,我可以使用其他列类型来执行此操作吗?
  • 如果以上任何一项都为“是”,我该怎么做?

【问题讨论】:

  • 唯一约束通常不能是有条件的。我会问为什么在某些情况下允许重复很重要,但在其他情况下不允许重复。
  • 不应允许已验证的成员使用重复的电子邮件地址。未经验证的成员没关系。
  • 如果没关系,无论如何都阻止重复地址。
  • 您可以设置复合 UNIQUE 索引,但我不确定这是否是您想要的。请记住,唯一约束中的 NULL 值将被忽略,因此如果您的辅助组件为 NULL,则允许您重复。您可能可以在email_addr, verified 上建立索引,如果verified 为NULL,您可以拥有无​​限的记录,但只有一个设置为verified=1
  • 这听起来像我想要的;出于某种原因,我确实在一个列上想到了NULL,但是……不,没关系,我用一个新列(布尔值)对其进行了测试,并且有效。感谢您的逻辑演练

标签: mysql enums


【解决方案1】:

我知道这个问题被问到已经 2 年了,但为了其他人有类似的挑战,需要做出回应。我几乎没有回答这个问题,直到我不得不查看更多评论和中提琴! @tadman评论。

我需要类似的实现,但用例不同,@tadman 评论正是我正在寻找的解决方案。这是他对您的问题的详细解决方案:

注意:在下面的步骤中,我假设表RegisteredMembers 已经存在且没有冲突数据。如果存在冲突的数据,您可能必须 通过执行TRUNCATE RegisteredMembers来截断表以清除所有数据

第 1 步:使用 member_statusemail_addr 两列在 RegisteredMembers 表上创建复合唯一键

ALTER TABLE `RegisteredMembers` ADD UNIQUE KEY `member_status_is_email_addr` (`member_status`,`email_addr`);

第 2 步:修改 member_status 列以使其可为空,并将其限制为仅 'V' 枚举值,因为任何其他枚举值将假定为 null 以允许其他插入相同的 email_addr 值强>

ALTER TABLE `RegisteredMembers` CHANGE `member_status` `member_status` ENUM('V') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;

第 3 步:这就是您的架构所需的全部修改。仅在验证后插入null 代替“B”、“A”、“P”和“V”。

我已经有一个类似的实现。如果您对此还有其他问题,请发表评论,我会尽快回复。

致谢:@tadman 评论这个问题

【讨论】:

    猜你喜欢
    • 2016-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-01
    • 1970-01-01
    • 2016-09-30
    相关资源
    最近更新 更多