Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MySQL compound index not being used

I have a large table from which I must select large amounts of rows.

The table stores call detail records (CDR's). Example:

+-------------+--------------+------+-----+---------------------+----------------+
| Field       | Type         | Null | Key | Default             | Extra          |
+-------------+--------------+------+-----+---------------------+----------------+
| id          | int(45)      | NO   | PRI | NULL                | auto_increment |
| calldate    | datetime     | NO   | MUL | 0000-00-00 00:00:00 |                |
| accountcode | varchar(100) | NO   |     |                     |                |
| other...    | varchar(45)  | NO   |     |                     |                |

Since my queries look for a customers calls in certain dates, I indexed calldate and accountcode together in a clustered index like so:

CREATE TABLE `cdr` (
  `id` int(45) NOT NULL AUTO_INCREMENT,
  `calldate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `accountcode` varchar(100) NOT NULL DEFAULT '',
   other fields...
PRIMARY KEY (`id`),
KEY `date_acc` (`calldate`,`accountcode`) USING BTREE
) ENGINE=MyISAM DEFAULT CHARSET=latin1

However, when executing the following query, the EXPLAIN result shows that only the datetime portion of the key is being used:

Query:

SELECT * 
FROM cdr
WHERE calldate > '2010-12-01'
  AND accountcode = 'xxxxxx';

EXPLAIN result:

+----+-------------+-------+-------+---------------+----------+---------+------+---------+----------+-------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+-------+-------+---------------+----------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | cdr   | range | date_acc      | date_acc | 8       | NULL | 3312740 |   100.00 | Using where |
+----+-------------+-------+-------+---------------+----------+---------+------+---------+----------+-------------+

It seems only the first 8 bytes (the date portion of the key) is being used. However the WHERE clause explicitly references both parts of the key with an AND, so in theory the full key should be used.

Should I create separate indexes for calldate and accountcode and let the query optimizer merge them? Why is the full index not being used?

Thanks for the help!

like image 347
Alex Recarey Avatar asked Jan 04 '11 22:01

Alex Recarey


People also ask

Why index is not being used in MySQL?

The Benefits and Drawbacks of Using Indexes in MySQLIndexes consume disk space. Indexes degrade the performance of INSERT, UPDATE and DELETE queries – when data is updated, the index needs to be updated together with it. MySQL does not protect you from using multiple types of indexes at the same time.

Why is my query not using index?

Answer: Oracle SQL not using an index is a common complaint, and it's often because the optimizer thinks that a full-scan is cheaper than index access.

How do I force an index in MySQL?

In case the query optimizer ignores the index, you can use the FORCE INDEX hint to instruct it to use the index instead. In this syntax, you put the FORCE INDEX clause after the FROM clause followed by a list of named indexes that the query optimizer must use.

What is compound index in MySQL?

Composite Index: MySQL lets you define indices on multiple columns, up to 16 columns. This index is called a Multi-column / Composite / Compound index. Let's say we have an index defined on 4 columns — col1 , col2 , col3 , col4 .


2 Answers

Short answer: You'd be able to use the index more effectively here if your key was (accountcode, calldate) instead of (calldate, accountcode).

The best way to understand the problem is by thinking of multi-column keys as being a concatenation of the different columns. As an example if column 1 had values 'A,B,C,D' and column 2 'W,X,Y,Z' you'd construct an index on 'A-W, B-X, C-Y, D-Z' etc. and put all of those into a B-tree.

To do a range query, you find the first successor of low end of the range, and iterate till you exceed the upper range. This means that you can only effectively use the index to do a range query on a suffix of the key.

like image 60
Vinay Pai Avatar answered Sep 20 '22 16:09

Vinay Pai


Since you're looking for a range of dates (> '2010-12-01'), I don't see how the optimizer could use the full index. The best it can do is scan the range of dates looking for the matching accountcode. Now, if you were looking for exactly one date and exactly one accountcode, then I'd expect the full index to be used.

like image 33
Joe Stefanelli Avatar answered Sep 18 '22 16:09

Joe Stefanelli