以前在網上看了一些資料,有些人說話不嚴謹,導致一直被誤導,最近在實際開發中發現一些結論有問題,因此特地整理了一下,防止下次繼續犯錯。
以下前提是有對這個字段建立索引(簡直廢話,沒建的肯定不會使用索引啊)
首先建立一張表:
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 全表掃描