3

我正在读取tick最大记录的值id。以下导致执行缓慢的查询有什么区别?

慢查询:

SELECT tick
FROM   eventlog
WHERE  id IN (SELECT max(id) FROM eventlog)

快速查询:

SELECT max(id) INTO @id
FROM   eventlog;

SELECT tick
FROM   eventlog
WHERE  id = @id;

架构

CREATE TABLE eventlog (
    id INT (11) NOT NULL AUTO_INCREMENT,
    tick INT NOT NULL,
    eventType_id INT NOT NULL,
    compType INT (10) UNSIGNED NOT NULL,
    compID INT (10) UNSIGNED NOT NULL,
    value_double DOUBLE NOT NULL,
    value_int INT (10),
    hierarchy_id VARCHAR (255) NOT NULL,
    PRIMARY KEY (id),
    INDEX htet (
        hierarchy_id,
        tick,
        eventType_id
    )
)
4

2 回答 2

5

因为in查询不使用索引,所以mysql会扫描所有记录来查找行。

来自http://dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html

B-Tree 索引特征

B 树索引可用于使用 =、>、>=、<、<= 或 BETWEEN 运算符的表达式中的列比较。

没有IN

哈希索引特征

哈希索引与刚才讨论的有些不同:

它们仅用于使用 = 或 <=> 运算符的相等比较(但速度非常快)。它们不用于查找值范围的比较运算符,例如 <。

也没有IN

正如@tombom 提到的,foo IN ('bar', 'bla')是 的缩写foo = 'bar' OR foo = 'bla',但是,我相信它们是不同的。于是我对一张有足够数据记录的表进行了测试,发现如下:

mysql> show columns from t_key;   
+-------+---------+------+-----+---------+----------------+
| Field | Type    | Null | Key | Default | Extra          |
+-------+---------+------+-----+---------+----------------+
| a     | int(11) | NO   | PRI | NULL    | auto_increment |
| b     | int(11) | YES  | MUL | NULL    |                |
+-------+---------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

mysql> select count(a) from t_key;
+----------+
| count(a) |
+----------+
|   989901 |
+----------+
1 row in set (0.00 sec)

mysql> explain select a from t_key where a in (select max(a) from t_key);  
+----+--------------------+-------+-------+---------------+---------+---------+------+--------+------------------------------+
| id | select_type        | table | type  | possible_keys | key     | key_len | ref  | rows   | Extra                        |
+----+--------------------+-------+-------+---------------+---------+---------+------+--------+------------------------------+
|  1 | PRIMARY            | t_key | index | NULL          | PRIMARY | 4       | NULL | 989901 | Using where; Using index     |
|  2 | DEPENDENT SUBQUERY | NULL  | NULL  | NULL          | NULL    | NULL    | NULL |   NULL | Select tables optimized away |
+----+--------------------+-------+-------+---------------+---------+---------+------+--------+------------------------------+
2 rows in set (0.00 sec)

mysql> explain select a from t_key where a =(select max(a) from t_key);
+----+-------------+-------+-------+---------------+---------+---------+-------+------+------------------------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra                        |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+------------------------------+
|  1 | PRIMARY     | t_key | const | PRIMARY       | PRIMARY | 4       | const |    1 | Using index                  |
|  2 | SUBQUERY    | NULL  | NULL  | NULL          | NULL    | NULL    | NULL  | NULL | Select tables optimized away |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+------------------------------+
2 rows in set (0.00 sec)

然后我尝试IN使用静态序列进行查询,它像@tombom 提到的那样工作:

mysql> explain select a from t_key where a in (100,200);
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
|  1 | SIMPLE      | t_key | range | PRIMARY       | PRIMARY | 4       | NULL |    2 | Using where; Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
1 row in set (0.00 sec)

mysql> explain select a from t_key where a=100 or a=200;
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
|  1 | SIMPLE      | t_key | range | PRIMARY       | PRIMARY | 4       | NULL |    2 | Using where; Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
1 row in set (0.00 sec)

我不知道mysql是否会在可能的情况下将IN查询转换为ORs一个(例如,在查询之前知道序列),我没有找到相关文档,但explain表明它确实在这种情况下扫描了表。

于 2012-09-18T07:14:06.940 回答
5

尝试查看查询计划,DBMS 在这种情况下可能无法使用索引。尝试将查询更改为:

SELECT tick
FROM   eventlog
WHERE  id = (SELECT max(id) FROM eventlog)

编辑 实际上可能有更好的方法。在上面的查询中,您执行了两个 INDEX ACCESS 操作(如果索引不是唯一的,则执行另外一个 INDEX RANGE SCAN)和一个 TABLE ACCESS。相反,您可以这样做:

SELECT tick
FROM   eventlog
ORDER BY id DESC
LIMIT 1

有了这个,应该有一个 INDEX ACCESS 加上一个 TABLE ACCESS。事实上,可能存在相当小的差异,因为 TABLE ACCESS 是显着更昂贵的操作,因此可能会在大数据集上看到差异。

于 2012-09-18T07:01:20.627 回答