MySQL复合索引没有被使用

发布于 2024-10-10 01:48:09 字数 2042 浏览 7 评论 0原文

我有一个大表,我必须从中选择大量行。

该表存储呼叫详细记录 (CDR)。示例:

+-------------+--------------+------+-----+---------------------+----------------+
| 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   |     |                     |                |

由于我的查询在某些日期查找客户呼叫,因此我在聚集索引中将 calldate 和 accountcode 一起索引,如下所示:

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

但是,在执行以下查询时,EXPLAIN 结果显示仅使用键的日期时间部分:

查询:

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

解释结果:

+----+-------------+-------+-------+---------------+----------+---------+------+---------+----------+-------------+
| 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 |
+----+-------------+-------+-------+---------------+----------+---------+------+---------+----------+-------------+

似乎只使用了前 8 个字节(密钥的日期部分)。然而,WHERE 子句使用 AND 显式引用键的两个部分,因此理论上应该使用完整键。

我应该为 calldate 和 accountcode 创建单独的索引并让查询优化器合并它们吗?为什么不使用完整索引?

感谢您的帮助!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

月棠 2024-10-17 01:48:09

简短回答:如果您的密钥是 (accountcode, calldate) 而不是 (calldate, accountcode),那么您可以在这里更有效地使用索引。

理解该问题的最佳方法是将多列键视为不同列的串联。举个例子,如果第 1 列的值为“A、B、C、D”,第 2 列为“W、X、Y、Z”,您将在“AW、BX、CY、DZ”等上构造一个索引,然后将所有将它们放入 B 树中。

要进行范围查询,您需要找到范围低端的第一个后继,然后迭代直到超出范围上限。这意味着您只能有效地使用索引对键的后缀进行范围查询。

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.

离线来电— 2024-10-17 01:48:09

由于您正在寻找一系列日期(>“2010-12-01”),因此我不知道优化器如何使用完整索引。它能做的最好的事情就是扫描日期范围以查找匹配的帐户代码。现在,如果您正在寻找确切的一个日期和一个帐户代码,那么我希望使用完整的索引。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文