Reputation: 11023
Saw this: Can you use index in mysql using "col1 OR col2"? but thought I could ask this related question to keep the discussion simple and to the point.
I have two tables: lists
and agree_and_disagree_count
.
lists
table has two timestamp
fields modified_on
and first_publish_date
. Both these fields have their own indexes.
agree_and_disagree_count
table also has a timestamp
field date_created
, which is also indexed.
In the following OR statement on fields in the same table, both the indexes are used.
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
However if the fields are from different tables, it looks like the indexes are not used:
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
Why?
I did the following:
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 |
+----+-------------+-------+-------+-----------------------------------------------+--------------+---------+------+------+--------------------------------+
Looks like full scan still happening?
Upvotes: 2
Views: 261
Reputation: 57408
Once the JOIN
on list_id
is under way, MySQL has no way to "link" the list identifier with the fields used in the query and saved in the index, for no index contains both the ID and those fields; and you can't run joins on the indexes.
Try creating these two indexes:
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);
and retry with EXPLAIN. On my system I tried creating two minimal tables with two records each:
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)
and then:
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>
As expected, I now get indexing on both tables.
Can you post the tables' structure and existing indexes or, even better, a SQLFiddle?
Upvotes: 3