18

我试图强制 MySQL 使用两个索引。我正在加入一个表,我想利用两个索引之间的交叉。具体术语是 Using intersect,这里是 MySQL 文档的链接:

http://dev.mysql.com/doc/refman/5.0/en/index-merge-optimization.html

有没有办法强制执行此操作?我的查询正在使用它(并且它加快了速度),但现在无论出于何种原因它都停止了。

这是我想要执行此操作的 JOIN。我希望查询使用的两个索引是 scs.CONSUMER_ID_1 和 scs_CONSUMER_ID_2

JOIN survey_customer_similarity AS scs
    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
4

2 回答 2

22

请参阅 MySQL 文档以获取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

正如 TheScrumMeister 在下面指出的那样,这取决于您的数据,是否可以同时使用两个索引。


这是一个示例,您需要强制表出现两次以控制查询执行和交集。

使用它来创建一个包含 >100K 记录的表,其中大约 1K 行与过滤器匹配i in (2,3),1K 行匹配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;

做的时候:

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

你正好得到 8 场比赛:

+-------+------+------+
| 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 |
+-------+------+------+

在上面的查询中使用EXPLAIN以获得:

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

即使我们FORCE INDEX在两个索引上添加查询,EXPLAIN也会返回完全相同的东西

要使其跨两个索引收集,然后将它们相交,请使用:

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

使用该查询explain来获得:

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

这证明索引正在被使用。但这可能会或可能不会更快,具体取决于许多其他因素。

于 2011-01-30T03:58:31.900 回答
1

MySQL 仅支持每个连接使用单个索引。如果您希望它在连接中使用两列作为索引,您应该在这两列上创建一个索引。请注意,这并不像看起来那么糟糕,因为 (a,b) 上的索引兼作 a 上的索引。

请参阅MySQL 手册

如果列不构成索引的最左前缀,则 MySQL 不能使用索引。

于 2011-01-30T07:28:21.210 回答