views:

205

answers:

3

Setup:

mysql> create table t(a integer unsigned,b integer unsigned);
mysql> insert into t(a,b) values (1,2),(1,3),(2,4);
mysql> create index i_t_a on t(a);
mysql> create index i_t_b on t(b);
mysql> explain select * from t where a=1 or b=4;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | t     | ALL  | i_t_a,i_t_b   | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

Is there something I'm missing?

Update

mysql> explain select * from t where a=1 or b=4;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | t     | ALL  | i_t_a,i_t_b   | NULL | NULL    | NULL | 1863 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

Version:

mysql> select version();
+----------------------+
| version()            |
+----------------------+
| 5.1.36-community-log |
+----------------------+

Has anyone ever successfully make index merge work for MySQL?

I'll be glad to see successful stories here:)

+3  A: 

I have no idea if this is the actual reason but I would think any DBMS worth its salt would see the "rows=3" property and just decide that it's not worth even looking at the indexes. The speed at which you can do a full table scan on three rows would make any other method moot.

Try to do the same thing with a few thousand rows and see if you get the same results.


From here, a commenter states that "the table I tested with resulted in the index-merge-union version not using any indexes in certain situations" although they don't seem to know what those situations are, exactly :-) That's probably something you can raise with the MySQL support groups (and developers) as well.

Just out of interest, what does the following query give you from EXPLAIN:

select * from t where a=1
union
select * from t where b=4;

And it may be that MySQL is evaluating whether to use index-union based on the data within the table itself. If there are only 2 variants of a and 3 variants of b, it may again decide that your query will return a large proportion of the rows anyway, so not bother with optimization.

You could try with both a large number of rows and a large variety of values in both a and b columns.

Keep in mind this is not based on my knowledge of MySQL, I've never seen the codebase or used the product. However, I have done a bit of work on a certain mainstream database product - so this advice is based on how I understand to do things efficiently, which may not be the case for MySQL specifically, and indeed may not be the case at all generally :-)

paxdiablo
See my update,this is not the reason
Can you give an demo where index_merge works?For your updated question,it's a **UNION** with two **ref**.The problem I'm with is not in certain situations,but in all situations...
No, not really, I'm not a MySQL user, just basing suggestions on my knowledge of DBMS internals in general (see update). That's why this answer is CW - it may be utter crap for your particular situation.
paxdiablo
Oh,you haven't actually used MySQL,but it's fine if you post a demo that works in other DBMS.If the same setting works in it,then it might be a bug or a feature MySQL hasn't implemented?
The MySQL optimizer is quite complex and takes a lot of things into account, including the cardinality of the columns as well so i think you're on the right track @paxdiablo. @user198729 With regards to the number of rows i think you may need a lot more before the optimizer decides to use indexes. 2000 rows each of about 8 bytes (2 integer columns) only adds up to roughly 16KB. A full table scan is probably still very efficient at this size and doesn't require an index for performance.
Jarod Elliott
@Jarod Elliott,can you post a demo actually works?I've never see a working demo yet.Don't reason everything by the size of table,see my second comment,the index is used even though only several records exist...
@user198729 I've just tested this using your exact table and indexes but with 100,000 rows just repeating your insert above. I changed about 10 rows to have more unique values for a and b and then ran your exact same query but using a couple of the values i changed. This resulted in an index_merge in the explain. The optimizer will look at the size of the data AND the uniqueness. I'm not sure how you populated the rest of the rows for your test, but if a large portion of the rows have a "1" in the "a" column then it will still do a full table scan as it has to read most of the table anyway.
Jarod Elliott
@Jarod Elliott,can you post the `explain` output as a result and also the output of `show index from table`?BTW,are you using MySQL?
+2  A: 

Long back:

show indexes from lesssong;

Table, Non_unique, Key_name, Seq_in_index, Column_name, Collation, Cardinality, Sub_part, Packed, Null, Index_type, Comment
'lesssong', 0, 'PRIMARY', 1, 'S_ID', 'A', 50000, , '', '', 'BTREE', ''
'lesssong', 1, 'idx_s_name', 1, 'S_NAME', 'A', 25000, 10, '', '', 'BTREE', ''
'lesssong', 1, 'idx_S_ARID', 1, 'S_ARID', 'A', 1315, , '', '', 'BTREE', ''
'lesssong', 1, 'idxFTS', 1, 'S_NAME', '', 1, , '', '', 'FULLTEXT', ''

Count = 50000

explain select * from lesssong where s_name='kv' or s_arid=4

1, 'SIMPLE', 'lesssong', 'index_merge', 'idx_s_name,idx_S_ARID,idxFTS', 'idx_s_name,idx_S_ARID', '12,4', '', 2, 'Using sort_union(idx_s_name,idx_S_ARID); Using where'

Structure:

'S_ID', 'int(10) unsigned', 'NO', 'PRI', '', 'auto_increment'
'S_ALID', 'int(10) unsigned', 'NO', '', '', ''
'S_ARID', 'int(10) unsigned', 'NO', 'MUL', '', ''
'S_NAME', 'varchar(100)', 'NO', 'MUL', '', ''
'S_LYRIC', 'text', 'NO', '', '', ''
'S_WRITER', 'varchar(45)', 'NO', '', '', ''
'S_LINK', 'varchar(255)', 'NO', '', '', ''

Even for you structure I got it work for me:

I added random 100 values:

insert into t(a,b) select ceil(rand()*5),ceil(rand()*30)

explain select * from t where a=1 or b=4;

id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra
1, 'SIMPLE', 't', 'index_merge', 'i_t_a,i_t_b', 'i_t_a,i_t_b', '5,5', '', 32, 'Using union(i_t_a,i_t_b); Using where'
kv
My Version: '5.1.33-community-log'
kv
Can you paste the schema of lesssong here?
The structure I've already shown you. And as this works with your schema as well for me.Main thing is data. Try to generate data for ur schema like i did using inserting random numbers. And then execute the same query.It should do. Depending on data mysql will decide what is optimal way to execute.
kv
It won't work if you run `insert into t(a,b) select ceil(rand()*5),ceil(rand()*30)` many times,maybe just 10.
10 is not many times!! And ofcourse mysql may not use the merge index for such data!! I have executed 100 times it worked for me!Let me see options to show that to you...
kv
okay so here I'm: 1) Go to http://www.db4free.net/phpMyAdmin-3.3.1/2) username: kedar password: indexmerge3) use database ksql4) execute: explain select * from t where a=1 or b=4;I hope you will see yourself.
kv
A: 

It's too bad there's not a way to force MySQL to use the merge, like how you can force it to use a particular index when it's choosing the wrong one by default (happens rarely but I've seen it and had to deal with it).

I'm guessing it's simply because your data does not have enough cardinality for MySQL to decide it's worth its time to use an index, let alone an index_merge. 1800 rows is nothing - really. Create at least a million rows, and make every single one of them unique. Then it will probably do what you want. With such a small table an index doesn't really do anything for you.

Sean