【问题标题】:Why does MySQL add 1 to ENUM values?为什么 MySQL 将 ENUM 值加 1?
【发布时间】:2013-06-27 21:19:18
【问题描述】:

我今天在尝试从 ENUM 字段select sum 时发现了一些奇怪的东西。它在值上加了 1!我使用的示例表如下所示:

x     y
_______
3  |  2
0  |  1

xy 都是ENUM ('0','1','2','3')

我的查询如下:

select sum(x), sum(y), sum(x+y) from myfield

结果是:

5    5   10

这很奇怪。这是否意味着这种行为是一致的?我可以使用类似的东西吗:

select sum(x - 1), sum(y - 1), sum((x-1)+(y-1)) from myfield

这会产生正确的结果,还是这种行为只对我的数据库服务器特别有影响?

谢谢。

编辑: 对于那些想知道我为什么使用 ENUM 的人,这是因为我使用的实际字段包含无法放入 tinyint 的“AR”。

【问题讨论】:

标签: mysql performance enums


【解决方案1】:

由于ENUM 字段实际上只是一个INT UNSIGNED,因此如果您对ENUMs 使用整数值,它将无法按预期工作。例如,您的ENUM'0' 在内部存储为数字1,而您的'1' 存储为数字2。令人惊讶的是,空字符串'' 在内部存储为0。这是一个没有按预期工作的示例:

mysql> CREATE TABLE enumtest (
    -> id int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
    -> a ENUM ('0','1','2','3') NOT NULL DEFAULT '0',
    -> i int unsigned NOT NULL DEFAULT 0
    -> );
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO enumtest SET a = 0, i=0;
Query OK, 1 row affected, 1 warning (0.01 sec)

mysql> SHOW WARNINGS;
+---------+------+----------------------------------------+
| Level   | Code | Message                                |
+---------+------+----------------------------------------+
| Warning | 1265 | Data truncated for column 'a' at row 1 |
+---------+------+----------------------------------------+
1 row in set (0.00 sec)

mysql> INSERT INTO enumtest SET a = '0', i=0;
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO enumtest SET a = 1, i=1;
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO enumtest SET a = '1', i=1;
Query OK, 1 row affected (0.00 sec)

mysql> SELECT a,0+a,i FROM enumtest;                   
+---+-----+---+
| a | 0+a | i |
+---+-----+---+
|   |   0 | 0 |
| 0 |   1 | 0 |
| 0 |   1 | 1 |
| 1 |   2 | 1 |
+---+-----+---+
4 rows in set (0.00 sec)

mysql> SELECT SUM(a),SUM(0+a), SUM(i) FROM enumtest;    
+--------+----------+--------+
| SUM(a) | SUM(0+a) | SUM(i) |
+--------+----------+--------+
|      4 |        4 |      2 |
+--------+----------+--------+
1 row in set (0.00 sec)

子句0+aENUM 强制转换为其基础UNSIGNED 值。这相当于CAST(a AS UNSIGNED)

【讨论】:

    【解决方案2】:

    这是因为在枚举列上执行SUM() 并没有达到您认为的效果。为枚举列类型存储的数据是枚举中的索引,该索引从 1 开始,mysql 将使用该索引SUM()

    您的表格在显示时如下所示:

    x     y
    _______
    3  |  2
    0  |  1
    

    这显示了您的枚举值 - 您恰好也将枚举值定义为数字。您可以将它们定义为例如ENUM ('blue','red','green','yellow') 它看起来像:

    x         y
    _______________
    yellow  |  green
    blue    |  red
    

    但是,这仅用于显示。该表的行中实际存储的是 枚举中的索引。

    列规范中列出的元素被分配索引 数字,从 1 开始。

    所以这些索引从 1 开始。SUM() 和其他聚合函数用于 ENUM 列的正是这些基础数据。没有对枚举值的隐式转换,您也将其定义为数字。

    即存储的数据是这些索引:

    x     y
    _______
    4  |  3
    1  |  2
    

    虽然它对 SUM 枚举没有真正意义,但 mysql 在使用 SUM() 时会聚合这些索引

    docs 是必读的。

    【讨论】:

    • 好吧,我正在使用 PhpMyAdmin,它建议我应该以这种方式优化我的数据库。所以从你的回复来看,我想减1是安全的,对吧?
    • @rtuner 在您的特定情况下,您可以。但是在这里使用 SUM() 会引起大家的困惑,可能包括你自己,将来会看这段代码的人。如果要存储数字,请使用 int/tinyint 或类似的列。不要使用枚举。
    • 我一直在认真考虑它,很难决定要做什么,因为生成信息的字段也可以包含非数字值。最佳选择可能是对非数字值使用 -3、-2 和 -1。
    • “索引从 1 开始”不正确。对于空字符串,此索引从 0 开始。
    • @RossSmithII 您指定的枚举索引从 1 开始。您说得对,索引 0 也是空字符串。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-30
    • 2023-03-18
    • 2016-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多