将数据类型从varchar更改为枚举

时间:2013-11-05 17:54:56

标签: mysql enums indexing

我有一个表,我最近将几个列的类型从varchar更改为enum(见下文)。我的应用程序在这两列上对此表进行查询,并且在进行类型更改后,我发现此查询的性能严重下降(我已经包含了下面的查询以及解释计划结果)。到目前为止,我一直无法在这里找到罪魁祸首,希望有人遇到这个问题并提出建议。

desc order_transmission_history;

+--------------------------+--------------+------+-----+---------------------+----------------+
| Field                    | Type         | Null | Key | Default             | Extra          |
+--------------------------+--------------+------+-----+---------------------+----------------+
| id  | int(11)                           | NO   | PRI | NULL                | auto_increment |
| transmission_id          | varchar(255) | YES  |     | NULL                |                |
| transmitter_type         | varchar(10)  | YES  | MUL | NULL                |                |
| initial_attempt_date     | timestamp    | NO   | MUL | CURRENT_TIMESTAMP   |                |
| most_recent_attempt_date | timestamp    | NO   |     | 0000-00-00 00:00:00 |                |
| most_recent_status       | varchar(16)  | YES  |     | NULL                |                |
+--------------------------+--------------+------+-----+---------------------+----------------+

索引为:KEY transmission_history_transmitter_status_datetransmitter_typemost_recent_statusinitial_attempt_date

explain SELECT * FROM order_transmission_history where transmitter_type = 'FAX_1' AND transmission_id = '' AND (most_recent_status is null or (most_recent_status not in ('SENT', 'ERROR')));

+----+-------------+----------------------+-------+-------------------------------------------------------------------------------------------+----------------------------------------------+---------+------+------+-------------+
| id | select_type | table                | type  | possible_keys                                                                             | key                                          | key_len | ref  | rows | Extra       |
+----+-------------+----------------------+-------+-------------------------------------------------------------------------------------------+----------------------------------------------+---------+------+------+-------------+
|  1 | SIMPLE      | transmission_history | range | transmission_history_transmitter_status_date | transmission_history_transmitter_status_date | 32      | NULL |  350 | Using where |
+----+-------------+----------------------+-------+-------------------------------------------------------------------------------------------+----------------------------------------------+---------+------+------+-------------+

现在,使用更改的数据类型:

+--------------------------------------+----------------------------------------------------------------------------------+------+-----+-------------------+----------------+
| Field                                | Type                                                                             | Null | Key | Default           | Extra          |
+--------------------------------------+----------------------------------------------------------------------------------+------+-----+-------------------+----------------+
| id                                   | int(11)                                                                          | NO   | PRI | NULL              | auto_increment |
| transmission_id                      | varchar(255)                                                                     | YES  |     | NULL              |                |
| initial_attempt_date                 | timestamp                                                                        | NO   | MUL | CURRENT_TIMESTAMP |                |
| most_recent_attempt_date             | timestamp                                                                        | YES  |     | NULL              |                |
| transmitter_type                     | enum('FAX_1','FAX_2','FAX_3','EMAIL')                                            | YES  | MUL | NULL              |                |
| most_recent_status                   | enum('NONE','PENDING','TRANSIENT_ERROR','ERROR','SENDING','SENT','SYSTEM_ERROR') | YES  |     | NULL              |                |
+--------------------------------------+----------------------------------------------------------------------------------+------+-----+-------------------+----------------+


explain SELECT * FROM order_transmission_history where transmitter_type = 'FAX_1' AND transmission_id = '' AND (most_recent_status is null or (most_recent_status not in ('SENT', 'ERROR')));

+----+-------------+----------------------------+------+----------------------------------------------+----------------------------------------------+---------+-------+--------+-------------+
| id | select_type | table                      | type | possible_keys                                | key                                          | key_len | ref   | rows   | Extra       |
+----+-------------+----------------------------+------+----------------------------------------------+----------------------------------------------+---------+-------+--------+-------------+
|  1 | SIMPLE      | order_transmission_history | ref  | transmission_history_transmitter_status_date | transmission_history_transmitter_status_date | 2       | const | 394992 | Using where |
+----+-------------+----------------------------+------+----------------------------------------------+----------------------------------------------+---------+-------+--------+-------------+

2 个答案:

答案 0 :(得分:0)

由于您在选择中使用

most_recent_status is null OR (most_recent_status not in ('SENT', 'ERROR'))

计划者不会使用您的密钥。因为没有办法使用带有where子句的密钥。

所以唯一可以使用的是

 transmitter_type = 'FAX_1' AND transmission_id = ''

但是在你的情况下,计划者认为你在索引中有很多带有这些值的行,所以没有优势使用索引。

您可以强制使用索引,但不要认为它会有所帮助。您可能需要考虑将查询重写为更具体的方法(例如,添加"按order_recent_attempt_date限制10"并首先使用most_recent_attempt_date创建密钥)

如果不在most_recent_status中使用空值(在枚举中放置'未定义'),则可以获得更多性能,并使用使用状态值的查询而不是用户设置的查询(在/不在其中)。 / p>

答案 1 :(得分:0)

根据我的经验,当你在枚举字段上有where not in<>时,MySQL 喜欢使用索引。请尝试翻转查询以检查显式值。

SELECT * FROM order_transmission_history
where transmitter_type = 'FAX_1'
AND transmission_id = ''
AND (most_recent_status is null or
(most_recent_status in
('NONE','PENDING','TRANSIENT_ERROR','SENDING','SYSTEM_ERROR')));