MySQL枚举类型与整数类型的比较

18

我有几个表格,其中的列可以是 ENUM 类型或 INT 类型。我倾向于始终使用整数类型,以为基于它进行搜索会更快。

例如,我的一个表格有一列:StatusType,它只有 4 个可能的值:CompletedIn ProgressFailedTodo

与其将上述值存储为 ENUM 字符串,我将它们分别存储为:

1234。然后在我的 PHP 代码中,我使用常量变量来定义这些值,如下所示:

define('COMPLETED', 1);
define('IN_PROGRESS', 2);
define('FAILED', 3);
define('TODO', 4);
现在我的问题是,我是不是做得对,或者我应该将其更改为 ENUM 类型并在查询中使用字符串进行比较?我有许多其他列只能拥有最大 4-5 个可能的值集合。

为什么不将它们存储为字符串在一个varchar列中呢? - karlingen
@karlingen,整数比字符串更快,对吗? - GGio
你是如何使用这个表格的?它主要是写入还是读取? - JNevill
@JNevill 主要是阅读,但也写了很多。 - GGio
使用PHP枚举:http://php.net/manual/en/class.splenum.php,阅读其他答案以了解原因。 - Joel Harkes
2个回答

20

MySQL中的枚举值看起来非常酷,但我不是它的粉丝。 它们仅限于255个值,因此如果您决定添加更多值,则可能会遇到限制。 此外,正如描述的那样,您需要将应用程序代码中的值与数据库中的值同步-这似乎是有潜在危险的。

此外,它们使某些未来更改变得更加困难。 例如,其他数据库不支持枚举。 如果要添加多语言支持,将代码嵌入到数据库的数据类型定义中会比较棘手。

更标准的方法是使用一个或多个引用表,通过join获取值。 您可以使用混合方法,在数据库中使用引用表。 然后,您可以将引用表加载到应用程序中,以获得从数字到字符串的映射,从而避免在代码中进行联接。


1
所以基本上需要另外创建一张名为 Statuses 的表,并且设定一个 AUTO_INCREMENT ID 作为主键,同时也将该 ID 设定为指向 StatusType 表的外键。此外,Statuses 表中还需有一个字符串表示它所代表的状态。虽然这样听起来是个不错的解决方案,但假若我需要选取“全部已完成”的记录时,我仍需要与参考表中的一个字符串进行比较,对吧? - GGio
我正在写同样的东西。将StatusType_Code/StatusType_Desc存储在单独的表中。在大表中使用代码,只有在需要文本值时才调用单独的文本表。 - JNevill
1
@GGio - 实际上,是否使用AUTO_INCREMENT与主题无关(虽然我建议不要使用,因为设置的值似乎并不特别动态)。 - Álvaro González
此外,在Auto_Increment上,您可能会发现将来想以某种方式对文本/描述进行编号。您可能会发现将它们编号为10、20、30、40很有价值,以防将来想要排序并添加新值到排序中而不必重新编号所有内容。AutoIncrement很好用,但在这里可能过于复杂且具有限制性。 - JNevill

6
你的观点有一定道理。 从性能角度来看,Enum非常糟糕: MySQL Enum performance advantage? 话虽如此,将INT的定义绑定到您的代码中也不是很好。 理想情况下,如果您按照正确的数据规范模式进行操作,您应该在数据库中定义INT的值,就像在另一个表格中一样,并使用定义的索引作为值进行分配。
参见:http://en.wikipedia.org/wiki/Database_normalization#Normal_forms 这样做的原因是数据可移植,并且不需要代码库读取即可使用(只需执行联接即可轻松转储CSV以供Excel使用)。
祝你好运。
SQL示例:
SELECT *, state.name AS state FROM students
JOIN states ON student.state_id = states.id

仅获取州名称。

或进行筛选:

SELECT * FROM students
JOIN states ON student.state_id = states.id
WHERE state.name = 'Maine' OR state.code = 'ME'

这个例子可能有些奇怪,但是其含义是INT类型数据较小而VARCHAR类型数据是可变长度的。如果每行存储的是'Maine'而不是'16',那么在处理数百万行数据时会产生很大的差异。此外,索引INT类型数据要比VARCHAR类型的快得多,所以查询速度更快,特别是当你已经提前知道数字并构建查询时避免使用JOIN。虽然这不是一种常见的做法,但是如果您想让查询速度更快并且可以确保假设值的有效性,就可以这么做。


假设我想选择所有“已完成”的记录。我会写出这样的代码:SELECT * FROM StatusType INNER JOIN Statuses ON SsID = StID WHERE SsName = 'Completed' 这仍然是在比较字符串,对吧?SsName 是参考表中的列名。 - GGio
@GGio,是的,这将在字符串上进行比较,但它要比较的表只有4或5条记录。然后它会使用ID连接回您的大表,因此您对大表的读取仍然很快。这里任何速度损失都只是由于JOIN。考虑重写您的示例SQL语句以使用where中的ID,以便您可以避免连接。只有当您实际需要将值带入前端供用户查看时,才联系键/值表。 - JNevill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接