MySQL:Enum vs Varchar-with-Index

时间:2018-03-09 07:54:03

标签: mysql database indexing database-design sqldatatypes

假设,我需要创建一个表,其中一列将具有来自此有限且永不改变的集合的值: '所有','本地' qa',' staging'和' production'。

在这种情况下使用enum数据类型看起来是一个合适的解决方案,但在阅读了this article和互联网上的其他一些主题后,我不鼓励使用它。所以,如果我想创建一个查找表保持evnname的组合是唯一的,那么什么是我在ENUM类型的列和具有VARCHAR类型的列之间的最佳选项,但是在其上创建了索引。

另外考虑到此表中的插入很少,我们希望此特定查询执行得更快:

SELECT `enabled` FROM `features`
WHERE `name` = 'some_featuere'
AND `env` IN('all', 'qa')
ORDER BY `enabled` ASC limit 1;

其中哪一个是更好的设计?为什么?

CREATE TABLE `features` (
  `id` INTEGER  NOT NULL AUTO_INCREMENT,
  `name` VARCHAR (50) NOT NULL,
  `env` ENUM('all', 'local', 'qa', 'staging', 'production') NOT NULL,
  `enabled` TINYINT(1) DEFAULT 0,
  `created_at` DATETIME,
  `updated_at` DATETIME,

  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_unq_features_name_env` (`name`,`env`)
);

OR

CREATE TABLE `features` (
  `id` INTEGER  NOT NULL AUTO_INCREMENT,
  `name` VARCHAR (50) NOT NULL,
  `env` VARCHAR(10) NOT NULL,
  `enabled` TINYINT(1) DEFAULT 0,
  `created_at` DATETIME,
  `updated_at` DATETIME,

  PRIMARY KEY (`id`),
  INDEX `idx_features_env` (`env`),
  UNIQUE KEY `idx_unq_features_name_env` (`name`,`env`)
);

3 个答案:

答案 0 :(得分:1)

您的问题的简短回答是“不”,因为您的查询将在两个方案中使用name / env上的索引。但是,如果我不得不选择其中一个,那么我会选择VARCHAR而不是ENUM作为两个邪恶的较小但我认为你的方法可能还有其他一些问题。

首先,VARCHAR选项只会复制该文章中提到的ENUM问题(即添加属性或相关数据),同时可能会损失您从ENUM获得的唯一优势,即数据完整性。您可以通过查找获得数据完整性,而无需ENUM的恶意。

其次,您可能会关注查询不存在的性能问题。它多久运行一次?它有多慢?就目前而言,你有一个NAME / ENV的索引,我想不到加速查询的唯一方法是覆盖索引,包括ENABLED,但我怀疑这是一个性能杀手,我怀疑你看到加入查询表的差别很小。

第三,除非一个功能一次只能在一个环境中或同时在所有环境中部署,否则'ALL'作为一个选项毫无意义。如果不成立,则无论何时要应用“ALL”选项,都必须删除与功能名称相关的所有其他记录。 “ALL”还可以防止在不同环境中有选择地启用/禁用功能或单独记录创建/更新事件。这引入了不需要存在的数据管理问题。

第四,虽然列IDNAMECREATED_ATUPDATED_AT都是看似直接与功能相关的属性。列ENVENABLED与部署功能的位置和方式有关。乍一看,这表明将这些数据存储在一个完全独立的表中(可能包含CREATED_ATUPDATED_AT,以指示它们何时首次部署和上次更新)。我个人将Feature,Environment和Feature_Environment作为单独的表与Feature_Environment中的外键一起使用到其他两个表。

答案 1 :(得分:1)

有一个宗教信仰'亲ENUM和反ENUM派系之间的战争。你已经阅读了其中一个' anti'文章。但许多"邪恶"在那篇文章中可能不适用于你的情况。

您的查询可以通过摆脱目前的PK id并将其替换为

来加速
PRIMARY KEY(name, env)

之后,不需要二级索引。

您需要在辅助密钥中查找,然后进入PK以获取第三列。之后,排序并交付一行。

更改PK可避免额外查找。并且应该没有"缺点"改变。

如果表中有数百万行,并且您可能要求每个env值的数千个候选者,那么这将更快,因为它不会收集"数千&# 34;行,排序,只提供一个。相反,它会获得两行并从中挑选:

    (   SELECT  `enabled`
            FROM  `features`
            WHERE  `name` = 'some_featuere'
              AND  `env` = 'all'
            ORDER BY  `enabled` ASC
            limit  1
    )
    UNION DISTINCT
    (  SELECT  `enabled`
            FROM  `features`
            WHERE  `name` = 'some_featuere'
              AND  `env` = 'qa'
            ORDER BY  `enabled` ASC
            limit  1 
    )
    ORDER BY  `enabled`
    LIMIT  1;

是的,ORDER BYLIMIT会重复出现。我不推荐这个OR - > UNION用于小型数据集,因为有许多步骤,每个步骤都有开销。

无论您使用ENUM还是其他方法,我的答案都适用。它确实假设InnoDB。

答案 2 :(得分:1)

这是一个不同的答案 - 使用SET数据类型。

以下是表格更改:

`env` SET('local', 'qa', 'staging', 'production') NOT NULL,
PRIMARY KEY (`name`)

没有辅助密钥,没有'all',每name只有一行。

然而,测试变得更加混乱。还是变得更简单?那是

AND `env` IN('all', 'qa')

- >

AND env = 'qa'

因为测试现在只有一件事。凌乱的all已经消失了。

如果所有这些都符合业务逻辑,则表和查询更简单,更快。

如果我对业务逻辑有误,请详细说明。可能有办法拯救此答案(使用SET而不是IN)。