Reputation: 5536
I am trying to speed up a query on a large table with WHERE clauses on two columns, as far as I can, MySQL is only using the ALERT_ID column.
Is there a way to rewrite this query using both indices?
SHOW_INDEX and EXPLAIN output is below.
show index from alert_hit;
+-----------+------------+-------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+-------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| alert_hit | 0 | PRIMARY | 1 | id | A | 15181402 | NULL | NULL | | BTREE | | |
| alert_hit | 1 | alert_id | 1 | alert_id | A | 20 | NULL | NULL | YES | BTREE | | |
| alert_hit | 1 | timestamp | 1 | timestamp | A | 446511 | NULL | NULL | YES | BTREE | | |
| alert_hit | 1 | data_source_id | 1 | data_source_id | A | 20 | NULL | NULL | YES | BTREE | | |
| alert_hit | 1 | filter_syndicated | 1 | filter_syndicated | A | 20 | NULL | NULL | YES | BTREE | | |
| alert_hit | 1 | unique_id | 1 | unique_id | A | 5060467 | NULL | NULL | YES | BTREE | | |
| alert_hit | 1 | date_created | 1 | date_created | A | 281137 | NULL | NULL | | BTREE | | |
| alert_hit | 1 | language | 1 | language | A | 20 | NULL | NULL | YES | BTREE | | |
| alert_hit | 1 | region | 1 | region | A | 42406 | NULL | NULL | YES | BTREE | | |
| alert_hit | 1 | market_rank | 1 | market_rank | A | 20 | NULL | NULL | YES | BTREE | | |
+-----------+------------+-------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
explain select count(id) as history FROM alert_hit force index(alert_id, timestamp) where alert_id in (9045,9046,9047,9048,9049,9050,9051,9052,9330,9332) AND timestamp between DATE_SUB( NOW(), INTERVAL 1*2 day) and DATE_SUB( NOW(), INTERVAL 1 day);
+----+-------------+-----------+-------+--------------------+----------+---------+------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+-------+--------------------+----------+---------+------+-------+-------------+
| 1 | SIMPLE | alert_hit | range | alert_id,timestamp | alert_id | 5 | NULL | 99578 | Using where |
+----+-------------+-----------+-------+--------------------+----------+---------+------+-------+-------------+
Upvotes: 2
Views: 1352
Reputation: 7590
You need to have one index on both fields
ALTER TABLE alert_hit ADD INDEX `IDX-alert_id-timestamp` (`alert_id`, `timestamp`);
Also MySQL will use the multi column index up to the first field for which there is a range condition in the WHERE clause, so in this case order matters and timestamp
should be last in the index.
As suggested by @spencer7593 selecting COUNT(1)
instead of count(id)
might also be better.
Upvotes: 5