Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is using an INT to select a Varchar index containing numbers much slower than using Strings?

I have a table that contains several thousand rows, having a Varchar column that contains numbers. Despite discussing why this column is not a numeric type then, selecting rows from that table showed a strange behavior.

Although there is an index on that column, using numeric strings to find a row is MUCH faster (0.01 secs) than using Ints (0.54 secs). What is the reason for this? It seems not to be able to cast and use the value for the index...

Am I overlooking something? It looks like it is not casting the Int to use it for the index? Do I have to give hints on index usage, or is there a database switch to accomplish this? Or if I misunderstand the Explain output, why is it so much slower then?

Table layout to show an example:

CREATE TABLE `example` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `stuff` varchar(45) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_stuff` (`stuff`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Here it is using the String for the index:

explain select * from example where stuff='200';
----+-------------+---------+------+---------------+-----------+---------+-------+------+--------------------------+
| id | select_type | table   | type | possible_keys | key       | key_len | ref   | rows | Extra                    |
+----+-------------+---------+------+---------------+-----------+---------+-------+------+--------------------------+
|  1 | SIMPLE      | example | ref  | idx_stuff     | idx_stuff | 137     | const |    1 | Using where; Using index |
+----+-------------+---------+------+---------------+-----------+---------+-------+------+--------------------------+

Here it looks like it does not cast the Int to a String to use for looking up the index:

explain select * from example where stuff=200;
+----+-------------+---------+-------+---------------+-----------+---------+------+------+--------------------------+
| id | select_type | table   | type  | possible_keys | key       | key_len | ref  | rows | Extra                    |
+----+-------------+---------+-------+---------------+-----------+---------+------+------+--------------------------+
|  1 | SIMPLE      | example | index | idx_stuff     | idx_stuff | 137     | NULL |    2 | Using where; Using index |
+----+-------------+---------+-------+---------------+-----------+---------+------+------+--------------------------+
like image 556
nico gawenda Avatar asked Apr 23 '12 02:04

nico gawenda


2 Answers

As stated in the manual:

For comparisons of a string column with a number, MySQL cannot use an index on the column to look up the value quickly. If str_col is an indexed string column, the index cannot be used when performing the lookup in the following statement:

SELECT * FROM tbl_name WHERE str_col=1;

The reason for this is that there are many different strings that may convert to the value 1, such as '1', ' 1', or '1a'.

If necessary, you can always CAST your integer to a string in order to take advantage of the index:

SELECT * FROM example WHERE stuff = CAST(200 AS CHAR);
like image 86
eggyal Avatar answered Oct 20 '22 19:10

eggyal


Warning: MySQL may also skip an index if its character set does not match, even if both values are CHAR. If the following query does not work:

SELECT * FROM example WHERE stuff = CAST(200 AS CHAR);

Then, get your database character set by running show variables like 'character_set_database'; and use that in a CONVERT statement as follows (this example assumes your database character set is latin1 -- replace that with your value of character_set_database):

SELECT * FROM example WHERE stuff = CONVERT(200 USING latin1);
like image 20
Kevin Borders Avatar answered Oct 20 '22 19:10

Kevin Borders