Force MySQL to use two indexes on a Join

前端 未结 2 545
攒了一身酷
攒了一身酷 2021-02-05 11:48

I am trying to force MySQL to use two indexes. I am joining a table and I want to utilize the cross between the two indexes. The specific term is Using intersect and here is a l

2条回答
  •  天命终不由人
    2021-02-05 12:35

    See MySQL Docs for FORCE INDEX.

    JOIN survey_customer_similarity AS scs 
    FORCE INDEX (CONSUMER_ID_1,CONSUMER_ID_2)
    ON
    cr.CONSUMER_ID=scs.CONSUMER_ID_2 
    AND cal.SENDER_CONSUMER_ID=scs.CONSUMER_ID_1 
    OR cr.CONSUMER_ID=scs.CONSUMER_ID_1 
    AND cal.SENDER_CONSUMER_ID=scs.CONSUMER_ID_2
    

    As TheScrumMeister has pointed out below, it depends on your data, whether two indexes can actually be used at once.


    Here's an example where you need to force the table to appear twice to control the query execution and intersection.

    Use this to create a table with >100K records, with roughly 1K rows matching the filter i in (2,3) and 1K rows matching j in (2,3):

    drop table if exists t1;
    create table t1 (id int auto_increment primary key, i int, j int);
    create index ix_t1_on_i on t1(i);
    create index ix_t1_on_j on t1(j);
    insert into t1 (i,j) values (2,2),(2,3),(4,5),(6,6),(2,6),(2,7),(3,2);
    insert into t1 (i,j) select i*2, j*2+i from t1;
    insert into t1 (i,j) select i*2, j*2+i from t1;
    insert into t1 (i,j) select i*2, j*2+i from t1;
    insert into t1 (i,j) select i*2, j*2+i from t1;
    insert into t1 (i,j) select i*2, j*2+i from t1;
    insert into t1 (i,j) select i*2, j*2+i from t1;
    insert into t1 (i,j) select i*2, j*2+i from t1;
    insert into t1 (i,j) select i*2, j*2+i from t1;
    insert into t1 (i,j) select i*2, j*2+i from t1;
    insert into t1 (i,j) select i*2, j*2+i from t1;
    insert into t1 (i,j) select i*2, j*2+i from t1;
    insert into t1 (i,j) select i*2, j*2+i from t1;
    insert into t1 (i,j) select i, j from t1;
    insert into t1 (i,j) select i, j from t1;
    insert into t1 (i,j) select 2, j from t1 where not j in (2,3) limit 1000;
    insert into t1 (i,j) select i, 3 from t1 where not i in (2,3) limit 1000;
    

    When doing:

    select t.* from t1 as t where t.i=2 and t.j=3 or t.i=3 and t.j=2
    

    you get exactly 8 matches:

    +-------+------+------+
    | id    | i    | j    |
    +-------+------+------+
    |     7 |    3 |    2 |
    | 28679 |    3 |    2 |
    | 57351 |    3 |    2 |
    | 86023 |    3 |    2 |
    |     2 |    2 |    3 |
    | 28674 |    2 |    3 |
    | 57346 |    2 |    3 |
    | 86018 |    2 |    3 |
    +-------+------+------+
    

    Use EXPLAIN on the query above to get:

    id | select_type | table | type  | possible_keys         | key        | key_len | ref  | rows | Extra
    1  | SIMPLE      | t     | range | ix_t1_on_i,ix_t1_on_j | ix_t1_on_j | 5       | NULL | 1012 | Using where
    

    Even if we add FORCE INDEX to the query on two indexes EXPLAIN will return the exact same thing.

    To make it collect across two indexes, and then intersect them, use this:

    select t.* from t1 as a force index(ix_t1_on_i)
    
    join t1 as b force index(ix_t1_on_j) on a.id=b.id
    
    where a.i=2 and b.j=3 or a.i=3 and b.j=2
    

    Use that query with explain to get:

    id | select_type | table | type  | possible_keys | key        | key_len | ref  | rows | Extra
    1  | SIMPLE      | a     | range | ix_t1_on_i    | ix_t1_on_i | 5       | NULL | 1019 | Using where
    1  | SIMPLE      | b     | range | ix_t1_on_j    | ix_t1_on_j | 5       | NULL | 1012 | Using where; Using index
    

    This proves that the indexes are being used. But that may or may not be faster depending on many other factors.

提交回复
热议问题