【问题标题】:MySQL not using indexes when fields from different tables are in OR condition当来自不同表的字段处于 OR 条件时,MySQL 不使用索引
【发布时间】:2012-12-21 16:42:08
【问题描述】:

看到这个:Can you use index in mysql using "col1 OR col2"? 但我想我可以问这个相关的问题,以使讨论简单明了。

我有两张桌子:listsagree_and_disagree_count

lists 表有两个timestamp 字段modified_onfirst_publish_date。这两个字段都有自己的索引。

agree_and_disagree_count 表还有一个timestamp 字段date_created,它也被索引了。

在以下对同一表中的字段的 OR 语句中,使用了两个索引。

mysql>  EXPLAIN SELECT * FROM lists l
    ->  WHERE (l.modified_on > '2013-01-07 12:50:51' OR
    ->   l.first_publish_date > '2013-01-07 12:50:51')\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: l
         type: index_merge
possible_keys: modifiedon,first_publish_date
          key: modifiedon,first_publish_date
      key_len: 4,9
          ref: NULL
         rows: 2
        Extra: Using sort_union(modifiedon,first_publish_date); Using where

但是,如果字段来自不同的表,则似乎没有使用索引:

mysql> EXPLAIN SELECT * FROM lists l
    -> JOIN agree_and_disagree_count adc ON adc.list_id=l.list_id
    -> WHERE (l.modified_on > '2013-01-07 12:50:51' OR
    -> adc.date_created > '2013-01-07 12:50:51')\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: adc
         type: ALL
possible_keys: list_id_type_id,idx_list_id,idx_date_created
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 5114907
        Extra:
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: l
         type: eq_ref
possible_keys: PRIMARY,modifiedon
          key: PRIMARY
      key_len: 4
          ref: adc.list_id
         rows: 1
        Extra: Using where

为什么?


我不知道如何在 cmets 中输入格式正确的回复,因此以下是对 @Iserni 的回应,他非常好心地帮助了我:

我做了以下事情:

use test;

-- create tables
create table lists (
list_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
modified_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
first_publish_date DATETIME NULL DEFAULT NULL,
primary key (list_id)
) ENGINE=InnoDB;

create table agree_and_disagree_count (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
list_id int unsigned not null,
date_created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
primary key (id)
) ENGINE=InnoDB;

-- create indices
create index index_modified_on on lists (modified_on);
create index index_first_publish_date on lists (first_publish_date);

create index index_data_created on agree_and_disagree_count (date_created);

-- load data
insert into lists (list_id, modified_on, first_publish_date) values 
(1, now(), now()),
(2, now(), now()),
(3, now(), now());

insert into agree_and_disagree_count (list_id, date_created) values 
(1, now()), (1, now()), (2, now());

-- query
EXPLAIN SELECT * FROM lists l JOIN agree_and_disagree_count adc 
ON adc.list_id=l.list_id WHERE (l.modified_on > '2013-01-01 12:50:51'
OR adc.date_created > '2013-01-07 11:50:51');

+----+-------------+-------+------+----------------------------------+------+---------+------+------+--------------------------------+
| id | select_type | table | type | possible_keys                    | key  | key_len | ref  | rows | Extra                          |
+----+-------------+-------+------+----------------------------------+------+---------+------+------+--------------------------------+
|  1 | SIMPLE      | l     | ALL  | PRIMARY,index_modified_on        | NULL | NULL    | NULL |    3 |                                |
|  1 | SIMPLE      | adc   | ALL  | index_data_created,index_list_id | NULL | NULL    | NULL |    3 | Using where; Using join buffer |
+----+-------------+-------+------+----------------------------------+------+---------+------+------+--------------------------------+

-- create new indexes suggested by Iserni
CREATE INDEX adc_test_ndx ON agree_and_disagree_count (list_id, date_created);
CREATE INDEX l_test_ndx ON lists (list_id, modified_on);

-- query
EXPLAIN SELECT * FROM lists l JOIN agree_and_disagree_count adc 
ON adc.list_id=l.list_id WHERE (l.modified_on > '2013-01-01 12:50:51'
OR adc.date_created > '2013-01-07 11:50:51');

+----+-------------+-------+-------+-----------------------------------------------+--------------+---------+------+------+--------------------------------+
| id | select_type | table | type  | possible_keys                                 | key          | key_len | ref  | rows | Extra                          |
+----+-------------+-------+-------+-----------------------------------------------+--------------+---------+------+------+--------------------------------+
|  1 | SIMPLE      | adc   | index | index_data_created,index_list_id,adc_test_ndx | adc_test_ndx | 8       | NULL |    3 | Using index                    |
|  1 | SIMPLE      | l     | ALL   | PRIMARY,index_modified_on,l_test_ndx          | NULL         | NULL    | NULL |    3 | Using where; Using join buffer |
+----+-------------+-------+-------+-----------------------------------------------+--------------+---------+------+------+--------------------------------+

似乎仍在进行全扫描?


在接受的答案下方查看@Iserni 的评论。

【问题讨论】:

    标签: mysql join indexing


    【解决方案1】:

    一旦list_id 上的JOIN 正在进行,MySQL 就无法将列表标识符与查询中使用并保存在索引中的字段“链接”,因为没有索引包含两者 em> ID 那些字段;并且您不能在索引上运行连接。

    尝试创建这两个索引:

    CREATE INDEX adc_test_ndx ON agree_and_disagree_count (list_id, date_created);
    CREATE INDEX l_test_ndx ON lists (list_id, modified_on);
    

    然后用 EXPLAIN 重试。在我的系统上,我尝试创建两个最小的表,每个表有两条记录:

    mysql> EXPLAIN SELECT * FROM lists l JOIN agree_and_disagree_count adc 
    ON adc.list_id=l.list_id WHERE (l.modified_on > '2013-01-01 12:50:51'
    OR adc.date_created > '2013-01-07 11:50:51')\G
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: l
             type: ALL
    possible_keys: NULL
              key: NULL
          key_len: NULL
              ref: NULL
             rows: 2
            Extra:
    *************************** 2. row ***************************
               id: 1
      select_type: SIMPLE
            table: adc
             type: ALL
    possible_keys: NULL
              key: NULL
          key_len: NULL
              ref: NULL
             rows: 2
            Extra: Using where; Using join buffer
    2 rows in set (0.00 sec)
    

    然后:

    mysql> CREATE INDEX adc_test_ndx ON agree_and_disagree_count (list_id, date_created);
    Query OK, 0 rows affected (0.00 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    
    mysql> CREATE INDEX l_test_ndx ON lists (list_id, modified_on);
    Query OK, 0 rows affected (0.00 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    
    mysql> EXPLAIN SELECT * FROM lists l JOIN agree_and_disagree_count adc 
    ON adc.list_id=l.list_id WHERE (l.modified_on > '2013-01-01 12:50:51'
    OR adc.date_created > '2013-01-07 11:50:51')\G
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: l
             type: index
    possible_keys: l_test_ndx
              key: l_test_ndx
          key_len: 9
              ref: NULL
             rows: 2
            Extra: Using index
    *************************** 2. row ***************************
               id: 1
      select_type: SIMPLE
            table: adc
             type: ref
    possible_keys: adc_test_ndx
              key: adc_test_ndx
          key_len: 5
              ref: test.l.list_id
             rows: 1
            Extra: Using where; Using index
    2 rows in set (0.00 sec)
    
    mysql>
    

    正如预期的那样,我现在可以在两个表上建立索引。

    您能否发布表的结构和现有索引,或者更好的是 SQLFiddle?

    【讨论】:

    • 那些索引没有帮助。我执行了:CREATE INDEX index_list_id_date_created ON agree_and_disagree_count (list_id, date_created);CREATE INDEX index_list_id_modified_on ON lists (list_id, modified_on);,但在 EXPLAIN 中仍然看到相同的输出。
    • 这一定与您的查询不对称有关——一张表中只有一行,另一张表中只有 500 万行。查询返回多少行?
    • 是的。原因是,由于记录如此之少,从数据库中检索行以检索first_publish_date 字段(由SELECT * 请求)的成本使得对lists 的搜索相当于表扫描(没有使用索引的优势)。如果您执行EXPLAIN SELECT l.list_id, l.modified_on, adc.*(不包括first_publish_date)或修改l_test_ndx 索引添加first_publish_date 作为最后一个字段,您将获得两个表的索引。我自己做了,因为我忘记在我的测试表定义中添加该字段。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-30
    • 2020-02-17
    • 1970-01-01
    • 2020-08-19
    • 2016-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多