关于MySQL IN LIKE OR使用索引的问题

以前在网上看了一些资料,有些人说话不严谨,导致一直被误导,最近在实际开发中发现一些结论有问题,因此特地整理了一下,防止下次继续犯错。

以下前提是有对这个字段建立索引(简直废话,没建的肯定不会使用索引啊)

首先建立一张表:

CREATE DATABASE `test_lkc` CHARATER SET utf8 COLLATE utf8_general_ci;

CREATE
TABLE `students` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `stuno` varchar(9) NOT NULL DEFAULT '' COMMENT '学号', `name` varchar(64) NOT NULL DEFAULT '' COMMENT '姓名', `major` varchar(255) NOT NULL DEFAULT '' COMMENT '专业', `class` int(10) unsigned NOT NULL DEFAULT '1' COMMENT '班级', `id_card` varchar(18) NOT NULL DEFAULT '' COMMENT '身份证号码', `memo` varchar(255) NOT NULL DEFAULT '' COMMENT '备注', PRIMARY KEY (`id`), UNIQUE KEY `stuno` (`stuno`) USING BTREE, UNIQUE KEY `id_card` (`id_card`) USING BTREE, KEY `major_class` (`major`,`class`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

 数据我是用php批量生成的

<?php
$pdo = new PDO("mysql:host=127.0.0.1;dbname=test_lkc","root","root");
//数据
$addList = [];
$majorList = ['CN','EN','JP','KO','RU','SV','CU','DE','FR','RO'];
$prefixList = ['A','B','C','D','E','F','G','H','I','J'];
for ($i=0;$i<1000;$i++){
    $row = [];
    $id = sprintf('%03d',$i);
    $row['stuno'] = $prefixList[$i/100] . '2017' . sprintf('%04d',$i + 1);
    $row['name'] = $prefixList[$id[0]] . $prefixList[$id[1]] . $prefixList[$id[2]];
    $row['major'] = $majorList[$id[0]];
    $row['class'] = $i%3 + 1;
    $row['id_card'] = '320106194910290' . $id;

    $addList[] = $row;
}

$res = addAll($pdo,$addList);
var_dump($res);

function addAll($pdo,$addList){
    //字段
    $sql = "INSERT INTO `students` (`" . implode("`,`",array_keys($addList[0])) . "`) VALUES ";
    //拼接SQL语句
    foreach ($addList as $value){
        $sql .= "('";
        $sql .= implode("','", $value);
        $sql .= "'),";
    }
    $sql = rtrim($sql,',');

    $res = $pdo->exec($sql);
    return $res ? $res : $pdo->errorInfo();
}

 

1.IN到底会不会使用索引?

答:会!

explain select * from students where id_card IN ('320106194910290999','320106194910290998');

如果没有走索引,那么可能是你的使用姿势不对,比如

(1)字符串类型没有加单引号,

explain select * from students where id_card IN (320106194910290999,320106194910290998);

(2)或者mysql认为使用全表扫描要比使用索引快(我自己遇到的是数据太少或者查询出来的结果很多的时候会出现,比如图中预估的行数是952),则不使用索引

explain select * from students where major IN ('RO','CN');

 

2.OR到底会不会使用索引?

答:会!

explain select * from students where id = 1 or stuno = 'A20170002';

如果没有走索引,那么肯定又是你的使用姿势不对,比如说

(1)两个条件里面其中一个字段没有建立索引;

explain select * from students where id = 1 or name = 'AAB';

(2)字符串类型的没有加单引号;

explain select * from students where id = 1 or id_card = 320106194910290999;

(3)mysql认为使用全表扫描要比使用索引快,则不使用索引

explain select * from students where id = 1 or major = 'CN';

 

3.LIKE会不会使用索引

答:LIKE 'xxx%'会;

(1)LIKE'%xxx'或者LIKE'%xxx%'不会

explain select * from students where stuno like 'A2017%';

explain select * from students where stuno like '%20170001';

 

(2)mysql认为使用全表扫描要比使用索引快,则不使用索引

explain select * from students where major like 'C%';

 

 

说来惭愧,我自己被错误的结论误导了那么久,希望大家引以为戒,网络上的答案也不一定就是对的,还是要自己多试试。实践才是检验真理的唯一标准

 

在查询语句前面加explain ,根据type和key来分析查询到底有没有使用索引

type排行:
    连接类型
    system          表只有一行
    const           表最多只有一行匹配,通用用于主键或者唯一索引比较时
    eq_ref          每次与之前的表合并行都只在该表读取一行,这是除了system,const之外最好的一种,
                    特点是使用=,而且索引的所有部分都参与join且索引是主键或非空唯一键的索引
    ref             如果每次只匹配少数行,那就是比较好的一种,使用=或<=>,可以是左覆盖索引或非主键或非唯一键
    fulltext        全文搜索
    ref_or_null     与ref类似,但包括NULL
    index_merge     表示出现了索引合并优化(包括交集,并集以及交集之间的并集),但不包括跨表和全文索引。
                    这个比较复杂,目前的理解是合并单表的范围索引扫描(如果成本估算比普通的range要更优的话)
    unique_subquery 在in子查询中,就是value in (select...)把形如“select unique_key_column”的子查询替换。
                    PS:所以不一定in子句中使用子查询就是低效的!
    index_subquery  同上,但把形如”select non_unique_key_column“的子查询替换
    range           常数值的范围
    index           a.当查询是索引覆盖的,即所有数据均可从索引树获取的时候(Extra中有Using Index);
                    b.以索引顺序从索引中查找数据行的全表扫描(无 Using Index);
                    c.如果Extra中Using Index与Using Where同时出现的话,则是利用索引查找键值的意思;
                    d.如单独出现,则是用读索引来代替读行,但不用于查找
    all             全表扫描

 

posted @ 2017-09-13 17:23  小昌君  阅读(5156)  评论(0编辑  收藏  举报