MySQL使用布尔条件执行慢查询

时间:2016-05-15 19:55:55

标签: mysql sql

表格示例:

       id       |      source     | removed
17D30437329A9B9 |                 |   0
M851X0LG81045F  | 17D30437329A9B9 |   0
QQG1RU1M8E5JHO  |                 |   0
QDVHFNFKF0Z80W  | 17D30437329A9B9 |   0
8BEFSFGUPBXJHV  |                 |   0

当我查询时:

SELECT  *
FROM    `uploads`
WHERE   (id = '17D30437329A9B9 ' OR `source` = '17D30437329A9B9 ')
AND     removed = 0

查询需要大约25秒(我有大约1700万行)。

但是当我跑这个时:

SELECT  *
FROM    `uploads`
WHERE   (id = '17D30437329A9B9 ' OR `source` = '17D30437329A9B9 ')

或者这个:

SELECT  *
FROM    `uploads`
WHERE   (`id` = '17D30437329A9B9 ')
AND     removed = 0

或那:

SELECT  *
FROM    `uploads`
WHERE   (`source` = '17D30437329A9B9 ')
AND     removed = 0

查询运行得很快。

为什么第一个查询运行速度很慢,我该如何解决?

编辑: EXPLAIN SELECT * FROM uploads WHERE (id = '17D30437329A9B9 ' OR source = '17D30437329A9B9 ') AND removed = 0;的结果:

+----+-------------+---------+------+-------------------------------------------------------+---------+---------+-------+---------+-------------+
| id | select_type | table   | type | possible_keys                                         | key     | key_len | ref   | rows    | Extra       |
+----+-------------+---------+------+-------------------------------------------------------+---------+---------+-------+---------+-------------+
|  1 | SIMPLE      | uploads | ref  | PRIMARY,removed,source,idx_member_selectFiles,id,id_2 | removed | 1       | const | 8829521 | Using where |
+----+-------------+---------+------+-------------------------------------------------------+---------+---------+-------+---------+-------------+

3 个答案:

答案 0 :(得分:1)

似乎没有复合索引。

运行此操作并再试一次

创建复合INDEX

ALTER TABLE `uploads`
ADD KEY (`id`,`source`,`removed`);

如果有效,请告诉我。

答案 1 :(得分:0)

具有OR条件的查询的MySQL执行计划有时不是最佳的。

我建议您重新编写查询以合并来自两个单独查询的结果。

SELECT u1.*
  FROM `uploads` u1
 WHERE u1.id      = '17D30437329A9B9 '
   AND u1.removed = 0

 UNION ALL

SELECT u2.*
  FROM `uploads` u2
 WHERE u2.source  = '17D30437329A9B9 '
   AND u2.removed = 0
   AND NOT (u2.id <=> '17D30437329A9B9 ')

每个SELECT都可以有效地使用最合适的索引。

第一个SELECT可以使用带有id前导列的索引。第二个SELECT可以使用带有source前导列的索引。

后续

问:如果我使用IN?喜欢WHERE(id IN('a','b')或来源IN('a','b'))AND删除= 0

答:我会使用相同的方法。

SELECT u1.*
  FROM `uploads` u1
 WHERE u1.id     IN ('17D30437329A9B9 ', ... )
   AND u1.removed = 0

 UNION ALL

SELECT u2.*
  FROM `uploads` u2
 WHERE u2.source IN ('17D30437329A9B9 ', ... )
   AND u2.removed = 0
   AND ( u2.id IS NULL OR u2.id NOT IN ('17D30437329A9B9 ', ... ) )

第二个SELECT上的最后一个条件是为了防止查询返回第一个SELECT已经返回的行。

使用NOT IN,只需确定列表中的值 none 为NULL。 (如果NOT IN列表包含NULL值,则没有行满足条件。)

如果id中的uploads列保证为NOT NULL,则可以消除对NULL的检查。 (我们没有看到任何表定义,因此我们无法判断id是否被定义为NOT NULL,因此编写查询以在更一般的情况下工作,而不是基于可能错误的假设。)

答案 2 :(得分:0)

B-Tree索引对于基数较低的列不好。在你的情况下,MySQL选择列removed的索引非常糟糕,因为只存在两个不同的值。

我怀疑永远不会在removed上看到索引的好处。删除removed上的索引。

位图索引而不是B树索引会很好。据我所知,MySQL不支持Bitmap-Indexes。

此外,(id, source)上的索引在这种情况下会有所帮助。