mysql> desc users;
+-------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| email | varchar(128) | NO | UNI | | |
| password | varchar(32) | NO | | | |
| screen_name | varchar(64) | YES | UNI | NULL | |
| reputation | int(10) unsigned | NO | | 0 | |
| imtype | varchar(1) | YES | MUL | 0 | |
| last_check | datetime | YES | MUL | NULL | |
| robotno | int(10) unsigned | YES | | NULL | |
+-------------+------------------+------+-----+---------+----------------+
8 rows in set (0.00 sec)
mysql> create index i_users_imtype_robotno on users(imtype,robotno);
Query OK, 24 rows affected (0.25 sec)
Records: 24 Duplicates: 0 Warnings: 0
mysql> explain select * from users where imtype!='0' and robotno is null;
+----+-------------+-------+------+------------------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+------------------------+------+---------+------+------+-------------+
| 1 | SIMPLE | users | ALL | i_users_imtype_robotno | NULL | NULL | NULL | 24 | Using where |
+----+-------------+-------+------+------------------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
但是这样,它被使用了:
mysql> explain select * from users where imtype in ('1','2') and robotno is null;
+----+-------------+-------+-------+------------------------+------------------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+------------------------+------------------------+---------+------+------+-------------+
| 1 | SIMPLE | users | range | i_users_imtype_robotno | i_users_imtype_robotno | 11 | NULL | 3 | Using where |
+----+-------------+-------+-------+------------------------+------------------------+---------+------+------+-------------+
1 row in set (0.01 sec)
此外,这个也没有使用索引:
mysql> explain select id,email,imtype from users where robotno=1;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | users | ALL | NULL | NULL | NULL | NULL | 24 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
答案 0 :(得分:3)
SELECT *
FROM users
WHERE imtype != '0' and robotno is null
单个连续的(imtype, robotno)
范围不满足此条件。
如果你有这样的记录:
imtype robotno
$ NULL
$ 1
0 NULL
0 1
1 NULL
1 1
2 NULL
2 1
,按(imtype, robotno)
排序,然后会返回记录1
,5
和7
,而其他记录则不会。
您需要创建此索引以满足条件:
CREATE INDEX ix_users_ri ON users (robotno, imptype)
并稍微重写您的查询:
SELECT *
FROM users
WHERE (
robotno IS NULL
AND imtype < '0'
)
OR
(
robotno IS NULL
AND imtype > '0'
)
,这将产生两个连续的块:
robotno imtype
--- first block start
NULL $
--- first block end
NULL 0
--- second block start
NULL 1
NULL 2
--- second block end
1 $
1 0
1 1
1 2
此索引也将提供此查询:
SELECT id, email, imtype
FROM users
WHERE robotno = 1
,由于同样的原因,现在没有任何索引提供。
实际上,此查询的索引:
SELECT *
FROM users
WHERE imtype in ('1', '2')
AND robotno is null
仅用于imtype
上的粗略过滤(using where
字段中的注释extra
),它不在robotno
的范围内
答案 1 :(得分:2)
您需要一个将robotno作为第一列的索引。您现有的索引是(imtype,robotno)。由于imtype不在where子句中,因此无法使用该索引。
(robotno,imtype)上的索引可以用于where子句中只有robotno的查询,也可以用于where子句中同时包含imtype和robotno的查询(但不是imtype本身)。
查看how MySQL uses indexes上的文档,查找有关多列索引和“最左前缀”的部分。
答案 2 :(得分:1)
FORCE INDEX (index_name)
之后 FROM users
。
答案 3 :(得分:0)
这是因为'robotno'可能是主键,它使用它而不是索引。
答案 4 :(得分:0)
数据库系统查询计划程序通过分析查询的where子句相对于索引的 selective 来确定是否执行索引扫描。 (索引也用于将表连接在一起,但此处只有users
。)
第一个查询有where imtype != '0'
。这将选择users
中的几乎所有行,假设您有大量不同的imtype
值。不等式运算符本质上是无选择性的。所以MySQL查询规划器在这里打赌,通过索引读取将无济于事,并且它可能只是对整个表进行顺序扫描,因为它可能必须这样做。
另一方面,如果你说where imtype ='0'
,等式是一个高度选择性的运算符,MySQL会认为通过只读取几个索引块就可以避免读取几乎所有{{1}的块。表本身。所以它会选择索引。
在你的第二个例子users
中,MySQL知道索引具有高度选择性(尽管只有where imtype in ('1','2')
的一半选择性),并且它再次打赌使用索引会导致正如你所发现的那样,收益很大。
在你的第三个例子where imtype = '0'
中,MySQL可能无法有效地使用where robotno=1
上的索引,因为它需要读入所有索引块才能找到users(imtype,robotno)
记录号:索引首先按robotno=1
排序,然后按imtype
排序。如果你在robotno
上有另一个索引,那么MySQL会热切地使用它。
作为一个脚注,如果您有两个索引,一个在users(robotno)
上,另一个在users(imtype)
上,并且您的查询位于users(imtype,robotno)
,则任一索引都会使您的查询变快,但MySQL可能会选择where imtype = '0'
,因为它更紧凑,需要从中读取更少的块。
我在这里非常简单。早期的数据库系统只会查看imtype的数据类型,并对查询的选择性进行粗略的猜测,但人们很快意识到给查询规划器提供了有趣的事实,例如表的总大小,每列中的ditinct值的数量等等将使它能够做出更明智的决定。例如,如果您有一个users(imtype)
表,其中users
只是每个'0'或'1',那么查询计划程序可能会选择索引,因为在这种情况下imtype
更具选择性
看看MySQL UPDATE STATISTICS语句,你会发现它的查询规划器必须是复杂的。出于这个原因,在使用FORCE语句来指示查询计划之前,我会犹豫不决。相反,使用UPDATE STATISTICS为查询计划程序提供改进的信息,以作出决定。
答案 5 :(得分:0)
您的索引超过users(imtype,robotno)
。要使用此索引,必须使用imtype
或imtype
和robotno
来限定行。您只是在查询中使用robotno
,因此无法使用此索引。