语句在后面


单表例题
EXPLAIN select SQL_NO_CACHE * FROM emp WHERE emp.age = 30
create INDEX idx_age on emp(age); #创建索引

EXPLAIN SELECT SQL_NO_CACHE * FROM emp where emp.age =30 AND deptId = 4 ;
create INDEX idx_age_deptid ON emp(age,deptId);

explain select SQL_NO_CACHE * from emp where emp.age = 30 and deptId = 4 AND name = ‘abcd’ ;
create INDEX idx_age_deptid_name ON emp(age,deptId,NAME);

全值匹配我最爱


create INDEX idx_age_deptid_name ON emp(age,deptId,’name’);
explain select SQL_NO_CACHE * from emp where deptId = 4 and emp.age = 30 AND name = ‘abcd’ ; #条件顺序调换了,但是仍然使用了索引

explain select SQL_NO_CACHE * from emp where emp.age = 30 AND name = ‘abcd’ ; #命中了age

explain select SQL_NO_CACHE * from emp where emp.deptid=4 AND name = ‘abcd’ ;#没有命中

最佳左前缀法则 :

如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。


这两条sql哪种写法更好
① : EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.name LIKE ‘abc%’

② : EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE LEFT(emp.name,3) = ‘abc’ #不要在在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描

create INDEX idx_name ON emp(NAME); #添加名字的索引 , 没有添加索引都差不多,添加了索引①更快

(原因 : 给字段加函数,计算,加类型转换,索引会失效 等)


EXPLAIN SELECT SQL_NO_CACHE FROM emp WHERE emp.age=30 AND *emp.deptId>20 AND emp.name = ‘abc’ ;

# 范围查询右边索引失效

create INDEX idx_age_deptid_name ON emp(age,deptId,’name’); #建索引把范围查询的字段建在前面,后面字段的索引全部失效

create INDEX idx_age_name_deptId ON emp(age,NAME,deptId); #应该把范围索引建在最后面


EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.NAME <> ‘abc’ ;

create INDEX idx_name on emp(NAME); #创建索引没有使用成功

mysql 在使用不等于(!= 或者<>)的时候无法使用索引会导致全表扫描


① : EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE age IS NULL;

② : EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE age IS NOT NULL ;

create INDEX idx_age on emp(age); #创建索引①成功使用,而②没有成功使用 ,

is not null 也无法使用索引,但是is null是可以使用索引的


EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.name LIKE ‘%abc%’;

create INDEX idx_name on emp(name);#索引没有使用,

like以通配符开头(‘%abc…’)mysql索引失效会变成全表扫描的操作


EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.name = 123 ;

create INDEX idx_name on emp(name);

# 字符串不加单引号索引失效


小练习 :

假设index(a,b,c)

Where语句 索引是否被使用
where a = 3 Y,使用到a
where a = 3 and b = 5 Y,使用到a,b
where a = 3 and b = 5 and c = 4 Y,使用到a,b,c
where b = 3 或者 where b = 3 and c = 4 或者 where c = 4 N
where a = 3 and c = 5 使用到a, 但是c不可以,b中间断了
where a = 3 and b > 4 and c = 5 使用到a和b, c不能用在范围之后,b断了
where a is null and b is not null is null 支持索引 但是is not null 不支持,所以 a 可以使用索引,但是 b不可以使用
where a <> 3 不能使用索引
where abs(a) =3 不能使用 索引
where a = 3 and b like ‘kk%’ and c = 4 Y,使用到a,b,c
where a = 3 and b like ‘%kk’ and c = 4 Y,只用到a
where a = 3 and b like ‘%kk%’ and c = 4 Y,只用到a
where a = 3 and b like ‘k%kk%’ and c = 4 Y,使用到a,b,c


#建表

  1. CREATE TABLE `dept` (
  2. `id` INT(11) NOT NULL AUTO_INCREMENT,
  3. `deptName` VARCHAR(30) DEFAULT NULL,
  4. `address` VARCHAR(40) DEFAULT NULL,
  5. ceo INT NULL ,
  6. PRIMARY KEY (`id`)
  7. ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  8. CREATE TABLE `emp` (
  9. `id` INT(11) NOT NULL AUTO_INCREMENT,
  10. `empno` INT NOT NULL ,
  11. `name` VARCHAR(20) DEFAULT NULL,
  12. `age` INT(3) DEFAULT NULL,
  13. `deptId` INT(11) DEFAULT NULL,
  14. PRIMARY KEY (`id`)
  15. #CONSTRAINT `fk_dept_id` FOREIGN KEY (`deptId`) REFERENCES `t_dept` (`id`)
  16. ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;


show variables like ‘log_bin_trust_function_creators’;
set global log_bin_trust_function_creators=1;


随机生成字符串

  1. DELIMITER $$
  2. CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
  3. BEGIN
  4. DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
  5. DECLARE return_str VARCHAR(255) DEFAULT '';
  6. DECLARE i INT DEFAULT 0;
  7. WHILE i < n DO
  8. SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
  9. SET i = i + 1;
  10. END WHILE;
  11. RETURN return_str;
  12. END $$

随机产生部门编号

  1. DELIMITER $$
  2. CREATE FUNCTION rand_num (from_num INT ,to_num INT) RETURNS INT(11)
  3. BEGIN
  4. DECLARE i INT DEFAULT 0;
  5. SET i = FLOOR(from_num +RAND()*(to_num -from_num+1)) ;
  6. RETURN i;
  7. END$$

往emp插入数据的存储过程

  1. DELIMITER $$
  2. CREATE PROCEDURE insert_emp( START INT , max_num INT )
  3. BEGIN
  4. DECLARE i INT DEFAULT 0;
  5. #set autocommit =0 把autocommit设置成0
  6. SET autocommit = 0;
  7. REPEAT
  8. SET i = i + 1;
  9. INSERT INTO emp (empno, NAME ,age ,deptid ) VALUES ((START+i) ,rand_string(6) , rand_num(30,50),rand_num(1,10000));
  10. UNTIL i = max_num
  11. END REPEAT;
  12. COMMIT;
  13. END$$

往dept插入数据的存储过程

  1. DELIMITER $$
  2. CREATE PROCEDURE `insert_dept`( max_num INT )
  3. BEGIN
  4. DECLARE i INT DEFAULT 0;
  5. SET autocommit = 0;
  6. REPEAT
  7. SET i = i + 1;
  8. INSERT INTO dept ( deptname,address,ceo ) VALUES (rand_string(8),rand_string(10),rand_num(1,500000));
  9. UNTIL i = max_num
  10. END REPEAT;
  11. COMMIT;
  12. END$$

DELIMITER ;
#执行操作
CALL insert_dept(10000);

CALL insert_emp(100000,500000);


select count() from dept ;
SELECT count(
) from emp ;


show INDEX FROM t_emp;

查询表中的索引
SELECT index_name FROM information_schema.STATISTICS WHERE table_name=’emp’ AND table_schema=’ssm’
AND index_name <>’PRIMARY’ AND seq_in_index = 1


批量删除某个表全部索引的函数

  1. DELIMITER $$
  2. CREATE PROCEDURE `proc_drop_index`(dbname VARCHAR(200),tablename VARCHAR(200))
  3. BEGIN
  4. DECLARE done INT DEFAULT 0;
  5. DECLARE ct INT DEFAULT 0;
  6. DECLARE _index VARCHAR(200) DEFAULT '';
  7. DECLARE _cur CURSOR FOR SELECT index_name FROM information_schema.STATISTICS WHERE table_schema=dbname AND table_name=tablename AND seq_in_index=1 AND index_name <>'PRIMARY' ;
  8. DECLARE CONTINUE HANDLER FOR NOT FOUND set done=2 ;
  9. OPEN _cur;
  10. FETCH _cur INTO _index;
  11. WHILE _index<>'' DO
  12. SET @str = CONCAT("drop index ",_index," on ",tablename );
  13. PREPARE sql_str FROM @str ;
  14. EXECUTE sql_str;
  15. DEALLOCATE PREPARE sql_str;
  16. SET _index='';
  17. FETCH _cur INTO _index;
  18. END WHILE;
  19. CLOSE _cur;
  20. END$$

执行删除非主键的索引
CALL proc_drop_index (‘ssm’,’emp’);