Here’s my table:
CREATE TABLE `wp_postmeta` (
`meta_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`post_id` bigint(20) unsigned NOT NULL DEFAULT '0',
`meta_key` varchar(255) DEFAULT NULL,
`meta_value` longtext,
`meta_value_integer` int(11) DEFAULT NULL,
`meta_value_date` date DEFAULT NULL,
PRIMARY KEY (`meta_id`),
KEY `post_id` (`post_id`),
KEY `meta_key` (`meta_key`),
KEY `post_meta_integer` (`meta_key`,`meta_value_integer`),
KEY `post_meta_date` (`meta_key`,`meta_value_date`)
) ENGINE=MyISAM AUTO_INCREMENT=2050 DEFAULT CHARSET=utf8
You can recognize it form WordPress but I added two extra columns to store the meta_value as an integer and one as a date. I’ve also added indices on (meta_key, meta_value_integer) and (meta_key, meta_value_date), the rest was there before.
Now I’m just wondering why is MySQL picking the meta_key
index and not the post_meta_integer
key on a query like this:
mysql> EXPLAIN SELECT * FROM wp_postmeta WHERE meta_key = 'price' AND meta_value_integer > 1000;
+----+-------------+-------------+------+-----------------------+----------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------+------+-----------------------+----------+---------+-------+------+-------------+
| 1 | SIMPLE | wp_postmeta | ref | meta_key,meta_integer | meta_key | 768 | const | 9 | Using where |
+----+-------------+-------------+------+-----------------------+----------+---------+-------+------+-------------+
1 row in set (0.00 sec)
I tried less than and BETWEEN too but no luck. If I change the comparison to =
though it uses the meta_integer key. Maybe it’s related to my dataset I don’t know. There are a lot of NULL values for meta_value_integer and dates too (where strings are used in meta_value for example).
So I’d like to know why does MySQL prefer the meta_key
index and what am I doing wrong. I’d really like to optimize the schema since there may be 200,000 different rows in posts meta and only 20 of them might be the prices. So ideally I’d like the meta_value_integer index to be smaller than the whole meta_key index. Is that possible?
Thanks!
You’re selecting on meta_key, so it’s quite logical that it uses that index. MySQL cannot use both indexes.
Solution: You can create a combined key on both columns (meta_key being the first column) and a separate key for the integer field. The combined key will be used when filtering on both columns (like you do now) or on meta_key, the first column, alone. The integer key will be used when you filter on the integer field alone.
If MySQL’s best guess at which index to use is not what you had in mind, you could try using an index hint.