【SQL】索引的创建与设计原则

本文介绍了SQL索引的创建与删除,强调了索引设计原则,包括适合创建索引的情况,如字段唯一性、频繁查询条件、group by和order by的列,以及UPDATE、DELETE操作涉及的列。同时指出不适合创建索引的情况,如数据量小的表、大量重复数据的列和无序值的字段。此外,还提到应避免冗余索引和定期删除不再使用的索引。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

索引的声明与使用

索引的创建

# 索引的创建
#法1:create table
# 隐式方式创建索引.在声明有主键约束、唯一性约束、外键约束的字段上,会自动添加相关索引,
CREATE DATABASE dbtest2;
USE dbtest2;
CREATE TABLE dept(
dept_id INT PRIMARY KEY AUTO_INCREMENT,
dept_name VARCHAR(20)
);

CREATE TABLE emp(
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(20) UNIQUE,
dept_id INT,
CONSISTENT  emp_dept_id_fk FOREIGN KEY(dept_id) REFERENCES dept(dept_id)
);

# 法2:显式方式创建
# ① 创建普通索引
CREATE TABLE book(
book_id INT,
book_name VARCHAR(100),
AUTHORS VARCHAR(100),
info VARCHAR(100),
COMMENT VARCHAR(100),
year_publication YEAR,
# 声明索引
INDEX idx_bname(book_name)
);

# 通过命令查看索引
#方式1:
SHOW CREATE TABLE book;
#方式2:
SHOW INDEX FROM book;

# 性能分析工具:
EXPLAIN SELECT * FROM book WHERE book_name = 'mysql';

# ② 创建唯一索引
# 声明有唯一索引的字段,在添加数据时,要保证唯一性,但是可以添加null值
CREATE TABLE book1(
book_id INT,
book_name VARCHAR(100),
AUTHORS VARCHAR(100),
info VARCHAR(100),
COMMENT VARCHAR(100),
year_publication YEAR,
# 声明索引
UNIQUE INDEX uk_idx_cmt(COMMENT)
);

SHOW INDEX FROM book1;

INSERT INTO book1(book_id,book_name,COMMENT)
VALUES(1,'MYSQL高级','适合有数据库开发经验的人员学习');

SELECT * FROM book1;

# ③ 主键索引
# 通过定义主键约束的方式定义主键索引
CREATE TABLE book2(
book_id INT PRIMARY KEY,
book_name VARCHAR(100),
AUTHORS VARCHAR(100),
info VARCHAR(100),
COMMENT VARCHAR(100),
year_publication YEAR
);

SHOW INDEX FROM book2;

# 通过删除主键约束的方式删除主键索引
ALTER TABLE book2
DROP PRIMARY KEY;

# ④ 创建单列索引
CREATE TABLE book3(
book_id INT,
book_name VARCHAR(100),
AUTHORS VARCHAR(100),
info VARCHAR(100),
COMMENT VARCHAR(100),
year_publication YEAR,
# 声明索引
UNIQUE INDEX idx_bname(book_name)
);

SHOW INDEX FROM book3;

# ⑤ 创建联合索引
CREATE TABLE book4(
book_id INT,
book_name VARCHAR(100),
AUTHORS VARCHAR(100),
info VARCHAR(100),
COMMENT VARCHAR(100),
year_publication YEAR,
# 声明索引
INDEX mul_bid_bname_info(book_id,book_name,info)
);

SHOW INDEX FROM book4;

# ⑥ 创建全文索引
CREATE TABLE test4(
id INT NOT NULL,
NAME CHAR(30) NOT NULL,
age INT NOT NULL,
info VARCHAR(255),
FULLTEXT INDEX futxt_idx_info(info(50))
);

SHOW INDEX FROM test4;

删除索引

# 索引删除
# 方式1:
SHOW INDEX FROM book1;

ALTER TABLE book1
DROP INDEX uk_idx_cmt;
# 添加auto_increment约束字段的唯一索引不能被删除

# 方式2:
SHOW INDEX FROM test4;
DROP INDEX futxt_idx_info ON test4;

# 联合索引中的某个字段删除
SHOW INDEX FROM book4;

ALTER TABLE book4
DROP COLUMN book_name;

ALTER TABLE book4
DROP COLUMN book_id;

ALTER TABLE book4
DROP COLUMN info;

索引设计原则

数据准备:

CREATE DATABASE atguigudb1;
USE atguigudb1;
#1.创建学生表和课程表
CREATE TABLE `student_info` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`student_id` INT NOT NULL ,
`name` VARCHAR(20) DEFAULT NULL,
`course_id` INT NOT NULL ,
`class_id` INT(11) DEFAULT NULL,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `course` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`course_id` INT NOT NULL ,
`course_name` VARCHAR(40) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

#函数1:创建随机产生字符串函数
DELIMITER //
CREATE FUNCTION rand_string(n INT)
RETURNS VARCHAR(255) #该函数会返回一个字符串
BEGIN
DECLARE chars_str VARCHAR(100) DEFAULT
'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
DECLARE return_str VARCHAR(255) DEFAULT '';
DECLARE i INT DEFAULT 0;
WHILE i < n DO
SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
SET i = i + 1;
END WHILE;
RETURN return_str;
END //
DELIMITER ;
# 创建时报错,进行下面的设置:
SELECT @@log_bin_trust_function_creators;
SET GLOBAL log_bin_trust_function_creators=1;

#函数2:创建随机数函数
DELIMITER //
CREATE FUNCTION rand_num (from_num INT ,to_num INT) RETURNS INT(11)
BEGIN
DECLARE i INT DEFAULT 0;
SET i = FLOOR(from_num +RAND()*(to_num - from_num+1)) ;
RETURN i;
END //
DELIMITER ;

# 存储过程1:创建插入课程表存储过程
DELIMITER //
CREATE PROCEDURE insert_course( max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0; #设置手动提交事务
REPEAT #循环
SET i = i + 1; #赋值
INSERT INTO course (course_id, course_name ) VALUES
(rand_num(10000,10100),rand_string(6));
UNTIL i = max_num
END REPEAT;
COMMIT; #提交事务
END //
DELIMITER ;
# 存储过程2:创建插入学生信息表存储过程
DELIMITER //
CREATE PROCEDURE insert_stu( max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0; #设置手动提交事务
REPEAT #循环
SET i = i + 1; #赋值
INSERT INTO student_info (course_id, class_id ,student_id ,NAME ) VALUES
(rand_num(10000,10100),rand_num(10000,10200),rand_num(1,200000),rand_string(6));
UNTIL i = max_num
END REPEAT;
COMMIT; #提交事务
END //
DELIMITER ;

CALL insert_course(100);
CALL insert_stu(1000000);

SELECT COUNT(*) FROM student_info;

哪些情况适合创建索引

① 字段的数值有唯一性的限制
② 频繁作为where查询条件的字段
③ 经常group by和order by的列
④ UPDATE、DELETE 的 WHERE 条件列
⑤ DISTINCT 字段需要创建索引

# 哪些情况适合创建索引
# ① 字段的数值有唯一性的限制
# ② 频繁作为where查询条件的字段
# 查看当前student_info表中的情况
SHOW INDEX FROM student_info;
# student_id 字段上没有索引
SELECT course_id,class_id,NAME,create_time,student_id
FROM student_info
WHERE student_id = 123110;	#829ms

#给student_id字段添加索引
ALTER TABLE student_info
ADD INDEX idx_sid(student_id);

# student_id字段上有索引的
SELECT course_id,class_id,NAME,create_time,student_id
FROM student_info
WHERE student_id = 123110;	#4ms

# ③ 经常group by和order by的列
# 有索引
SELECT student_id,COUNT(*) AS num
FROM student_info
GROUP BY student_id LIMIT 100;	#3ms

# 删除索引idx_sid
DROP INDEX idx_sid ON student_info;

#无索引
SELECT student_id,COUNT(*) AS num
FROM student_info
GROUP BY student_id LIMIT 100;	#1300ms

# 对于两个字段有无索引的对比
SHOW INDEX FROM student_info;

# 添加单列索引
ALTER TABLE student_info
ADD INDEX idx_sid(student_id);

ALTER TABLE student_info
ADD INDEX idx_cre_time(create_time);

EXPLAIN SELECT student_id,COUNT(*) AS num FROM student_info
GROUP BY student_id
ORDER BY create_time DESC
LIMIT 100;	# 6min 用了idx_sid

SELECT @@sql_mode;

# 添加联合索引
# 当我们使用 GROUP BY 对数据进行分组查询,或者使用 ORDER BY 对数据进行排序的时候,
# 就需要对分组或者排序的字段进行索引。
ALTER TABLE student_info
ADD INDEX idx_sid_cre_time(student_id,create_time DESC);

EXPLAIN SELECT student_id,COUNT(*) AS num FROM student_info
GROUP BY student_id
ORDER BY create_time DESC
LIMIT 100;	#608ms  	查询速度更快,使用了联合索引

#
ALTER TABLE student_info
ADD INDEX idx_cre_time_sid(create_time DESC,student_id);	# 改变联合索引字段顺序

DROP INDEX idx_sid_cre_time ON student_info;	
# 删除之前创建的sid打头的联合索引,再次执行select操作,发现实际使用的仍是idx_sid索引,
# 并非新创建的联合索引idx_cre_time_sid

# ④ UPDATE、DELETE 的 WHERE 条件列
UPDATE student_info SET student_id = 10002
WHERE NAME = '462eed7ac6e791292a79'; 	#2.274s

SHOW INDEX FROM student_info;
# 添加索引
ALTER TABLE student_info
ADD INDEX idx_name(NAME);

UPDATE student_info SET student_id = 10001
WHERE NAME = '462eed7ac6e791292a79'; 	#0.002s

# ⑤ DISTINCT 字段需要创建索引
# 索引会按顺序对数据排序,所以在去重时会快很多

# ⑥ 多表 JOIN 连接操作时,创建索引注意事项
# 连接表的数量尽量不要超过 3 张,因为每增加一张表就相当于增加了一次嵌套的循环,数量级增长会非常快,严重影响查询的效率。
# 对 WHERE 条件创建索引,因为 WHERE 才是对数据条件的过滤。如果在数据量非常大的情况下,没有 WHERE 条件过滤是非常可怕的。
# 对用于连接的字段创建索引,并且该字段在多张表中的类型必须一致。比如 course_id 在student_info 表和 course 表中都为 int(11) 类型,而不能一个为 int 另一个为 varchar 类型。

EXPLAIN SELECT student_info.course_id, NAME, student_info.student_id, course_name
FROM student_info JOIN course
ON student_info.course_id = course.course_id
WHERE NAME = '462eed7ac6e791292a79';

# ⑦ 使用列的类型小的创建索引
# ⑧ 使用字符串前缀创建索引
# ⑨ 区分度高(散列性高)的列适合作为索引
# ⑩ 使用最频繁的列放到联合索引的左侧
# 补充:在多个字段都要创建索引的情况下,联合索引优于单值索引

注意:
在这里插入图片描述

不适合创建索引的情况

  1. 在where中使用不到的字段,不要设置索引
  2. 数据量小的表最好不要使用索引
  3. 大量重复数据的列上不要建立索引
    当数据重复度大,比如高于 10% 的时候,也不需要对这个字段使用索引。
  4. 避免对经常更新的表创建过多的索引
  5. 不建议用无序的值作为索引
    例如身份证、UUID(在索引比较时需要转为ASCII,并且插入时可能造成页分裂)、MD5、HASH、无序长字
    符串等。
  6. 删除不再使用或者很少使用的索引
  7. 不要定义冗余或重复的索引
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值