mysql索引的使用[下]

接着上篇,我们继续来探究索引。这次我们主要来探究关于联合索引的使用和联合、多表查询的规范。

继续看一下数据:

mysql> select * from student order by ID desc;
+----+-------------+-----------+------------+
| ID | CName       | ClassName | SchoolName |
+----+-------------+-----------+------------+
  | rockderia26| 一年二班   | 拓荒学院     |
  | rockderia25| 一年二班   | 拓荒学院     |
  | rockderia24| 一年一班   | 拓荒学院     |
  | rockderia23| 一年一班   | 拓荒学院     |
  | rockderia22| 一年一班   | 拓荒学院     |
  | rockderia21| 一年一班   | 拓荒学院     |
  | rockderia14| 一年二班   | 冷锋中学     |
  | rockderia13| 一年二班   | 冷锋中学     |
  | rockderia12| 一年二班   | 冷锋中学     |
  | rockderia11| 一年一班   | 冷锋中学     |
  | rockderia08| 一年二班   | 花园中学     |
  | rockderia07| 一年二班   | 花园中学     |
  | rockderia06| 一年二班   | 花园中学     |
  | rockderia05| 一年二班   | 花园中学     |
  | rockderia04| 一年二班   | 花园中学     |
  | rockderia03| 一年二班   | 花园中学     |
  | rockderia02| 一年二班   | 花园中学     |
  | rockderia01| 一年二班   | 花园中学     |
  | rockderia8  | 一年一班  | 花园中学     |
  | rockderia7  | 一年一班  | 花园中学     |
  | rockderia6  | 一年一班  | 花园中学     |
  | rockderia5  | 一年一班  | 花园中学     |
  | rockderia4  | 一年一班  | 花园中学     |
  | rockderia3  | 一年一班  | 花园中学     |
  | rockderia2  | 一年一班  | 花园中学     |
  | rockderia1  | 一年一班  | 花园中学     |
+----+-------------+-----------+------------+
 rows in set (0.00 sec)
mysql> SHOW CREATE TABLE student;
+---------+------------------------------------------------------------------------
--------------------------------------------------------------------------------+
| Table | Create Table
|
+---------+------------------------------------------------------------------------
--------------------------------------------------------------------------------+
| student | CREATE TABLE `student` (
`ID` ) NOT NULL AUTO_INCREMENT COMMENT '自增长主键',
`CName` ) DEFAULT NULL,
`ClassName` ) DEFAULT NULL,
`SchoolName` ) DEFAULT NULL,
PRIMARY KEY (`ID`),
KEY `NameClassSchool` (`CName`,`ClassName`,`SchoolName`)
) ENGINE DEFAULT CHARSET=utf8 |
+---------+------------------------------------------------------------------------
--------------------------------------------------------------------------------+

我们看到一条联合索引 KEY `NameClassSchool` (`CName`,`ClassName`,`SchoolName`)

我们来执行以下语句:

EXPLAIN SELECT * FROM student WHERE cname = "rockderia";
mysql> EXPLAIN SELECT * FROM student WHERE cname = "rockderia";
+----+-------------+---------+------+-----------------+-----------------+---------+-------+------+--------------------------+
| id | select_type | table   | type | possible_keys   | key             | key_len | ref   | rows | Extra                    |
+----+-------------+---------+------+-----------------+-----------------+---------+-------+------+--------------------------+
            | Using where; Using index |
+----+-------------+---------+------+-----------------+-----------------+---------+-------+------+--------------------------+

可以看到索引起了作用。只遍历了一行数据。

我们再来试一下:

mysql> EXPLAIN SELECT * FROM student WHERE classname = "一年一班";
+----+-------------+---------+-------+---------------+-----------------+---------+------+------+--------------------------+
| id | select_type | table   | type  | possible_keys | key             | key_len | ref  | rows | Extra                    |
+----+-------------+---------+-------+---------------+-----------------+---------+------+------+--------------------------+
          | Using where; Using index |
+----+-------------+---------+-------+---------------+-----------------+---------+------+------+--------------------------+

我们看到遍历了所有的数据。作为联合索引的几个字段,如果拿来单独使用,有些生效有些却不生效。其实联合索引有一套使用规则。即索引生效多少取决于按照索引字段的顺序从左往右提供多少。

比如:一则联合索引 key(a,b,c)

如果单独使用 a 那么索引生效 生效区域就是a。
如果单独使用 b 那么索引就不会生效。因为使用b的前提先使用a,也就是 同时使用ab 索引也是生效的。

如果不考虑范围查找,那么联合索引 a,b,c 能生效的使用组合就是 a ab abc

索引在设置联合索引的时候一定要注意这个顺序。设置 a,b,c联合索引,就相当于设置的单独的a索引和另一则联合索引啊a,b。要利用这个特性使一则联合索引的价值最大化。

接下来我们讨论范围查找:

mysql> EXPLAIN SELECT * FROM student WHERE cname LIKE "rockderia1%" ;
+----+-------------+---------+-------+-----------------+-----------------+---------+------+------+--------------------------+
| id | select_type | table   | type  | possible_keys   | key             | key_len | ref  | rows | Extra                    |
+----+-------------+---------+-------+-----------------+-----------------+---------+------+------+--------------------------+
            | Using where; Using index |
+----+-------------+---------+-------+-----------------+-----------------+---------+------+------+--------------------------+
 row in set (0.00 sec)

我们发现索引是生效的,凡是满足范围查找规则的索引所对应的行被遍历。一共5则。我们继续追加:

EXPLAIN SELECT * FROM student WHERE cname LIKE "rockderia1%" AND classname = "一年一班";

如果遍历行数缩短为2行,那么就说明classname这条索引生效了。

mysql> EXPLAIN SELECT * FROM student WHERE cname LIKE "rockderia1%" AND classname = "一年一班";
+----+-------------+---------+-------+-----------------+-----------------+---------+------+------+--------------------------+
| id | select_type | table   | type  | possible_keys   | key             | key_len | ref  | rows | Extra                    |
+----+-------------+---------+-------+-----------------+-----------------+---------+------+------+--------------------------+
           | Using where; Using index |
+----+-------------+---------+-------+-----------------+-----------------+---------+------+------+--------------------------+
 row in set (0.00 sec)

运行后发现,然并卵。所以一旦使用模糊查询,那么后续的索引就不会生效了。

当然,模糊查询也有这个 左 的原则。比如一旦使用 select * from student where cname like "%deria01"; 将不会有任何索引生效

我们插入一则数据:

38 | rockderia1 | 一年二班 | 花园中学 |

这时候,rockderia1 出现两个。我们执行:

EXPLAIN SELECT * FROM student WHERE cname = "rockderia1";
+----+-------------+---------+------+-----------------+-----------------+---------+-------+------+--------------------------+
| id | select_type | table   | type | possible_keys   | key             | key_len | ref   | rows | Extra                    |
+----+-------------+---------+------+-----------------+-----------------+---------+-------+------+--------------------------+
      | Using where; Using index |
+----+-------------+---------+------+-----------------+-----------------+---------+-------+------+--------------------------+
mysql> EXPLAIN SELECT * FROM student WHERE cname = "rockderia1" AND classname LIKE "一年二%";
+----+-------------+---------+-------+-----------------+-----------------+---------+------+------+--------------------------+
| id | select_type | table   | type  | possible_keys   | key             | key_len | ref  | rows | Extra                    |
+----+-------------+---------+-------+-----------------+-----------------+---------+------+------+--------------------------+
           | Using where; Using index |
+----+-------------+---------+-------+-----------------+-----------------+---------+------+------+--------------------------+

由此关于这个左值的使用一目了然。

下面我们来试一下联合查询。我们创建一个教师表。

mysql> show create table teacher;
+---------+----------------------------------------------
| Table | Create Table
+---------+----------------------------------------------
| teacher | CREATE TABLE `teacher` (
`id` ) NOT NULL AUTO_INCREMENT,
`classname` ) DEFAULT NULL,
`cname` ) DEFAULT NULL,
`schoolname` ) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE DEFAULT CHARSET=utf8 |
+---------+----------------------------------------------
 row in set (0.00 sec)

加一些基础数据:

+----+-----------+--------+------------+
| id | classname | cname | schoolname  |
+----+-----------+--------+------------+
| 1  | 一年一班   | lily1  | 花园中学     |
| 2  | 一年一班   | lily2  | 花园中学     |
  | 一年二班   | lily3  | 花园中学     |
  | 一年二班   | lily11 | 花园中学     |
  | 一年二班   | lily12 | 冷锋中学     |
+----+-----------+--------+------------+

我们试一下如下语句:

EXPLAIN SELECT * FROM student s JOIN teacher t ON s.classname = t.classname;

+----+-------------+-------+-------+---------------+-----------------+---------+------+------+-----------------------------------------------------------------+
| id | select_type | table | type  | possible_keys | key             | key_len | ref  | rows | Extra                                                           |
+----+-------------+-------+-------+---------------+-----------------+---------+------+------+-----------------------------------------------------------------+
      | NULL                                                            |
          | Using where; Using index; Using join buffer (Block Nested Loop) |
+----+-------------+-------+-------+---------------+-----------------+---------+------+------+-----------------------------------------------------------------+

我们看到,在t表中每次遍历5行数据,在s表遍历了27行数据。索引似乎都没有生效。我们改变以下语句:

mysql> EXPLAIN SELECT * FROM student s JOIN teacher t ON t.id = s.id;
+----+-------------+-------+--------+---------------+---------+---------+-----------+------+-------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref       | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+-----------+------+-------+
      | NULL  |
             | NULL  |
+----+-------------+-------+--------+---------------+---------+---------+-----------+------+-------+

我们看到,作为关联条件双方如果都是变量,那么主表的索引会生效。但是从表不会从这条语句上生效。如果我们再追加:

EXPLAIN ;
+----+-------------+-------+--------+---------------+---------+---------+-----------+------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref       | rows | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+-----------+------+-------------+
      | Using where |
             | NULL        |
+----+-------------+-------+--------+---------------+---------+---------+-----------+------+-------------+

我们就会发现从表的索引也生效了,不过条件需要额外追加。如果条件不符合,那么:

mysql;
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                                               |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
  | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | Impossible WHERE noticed after reading const tables |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+

索引引擎会直接得知想要搜索的目标行不存在。不进行任何遍历。

当然,如果从表的搜索条件不是索引,那么就是另外一种情况:

mysql> explain select * from student s join teacher t on s.cname = t.cname and t.cname = "lily2";
+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+----------------------------------------------------+
| id | select_type | table | type | possible_keys   | key             | key_len | ref   | rows | Extra                                              |
+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+----------------------------------------------------+
        | const | 1    | Using where; Using index                           |
      | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+----------------------------------------------------+

如果是左连接的话,就又是另外一副景象了。因为左连接对于主表没有筛选工作,这就需要所有主表的数据进行操作:

mysql> explain select * from student s left join teacher t on s.cname = t.cname;
+----+-------------+-------+-------+---------------+-----------------+---------+------+------+----------------------------------------------------+
| id | select_type | table | type  | possible_keys | key             | key_len | ref  | rows | Extra                                              |
+----+-------------+-------+-------+---------------+-----------------+---------+------+------+----------------------------------------------------+
          | Using index |
      | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+-------+---------------+-----------------+---------+------+------+----------------------------------------------------+

就像这般。在已有的索引系统上写出面对新需求效率更高的查询语句,显得格外重要。

上一篇:《javascript设计模式》读书笔记四(单例模式)


下一篇:JAVA反射中的getFields()方法和getDeclaredFields ()方法的区别