INSERT INTO book1(book_id,book_name,COMMENT) VALUES(1,'Mysql高级','适合有数据库开发经验的人员学习');
INSERT INTO book1(book_id,book_name,COMMENT) VALUES(2,'Mysql高级',NULL);
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;
#分析 EXPLAIN SELECT * FROM book4 WHERE book_id = 1001 AND book_name = 'mysql';
EXPLAIN SELECT * FROM book4 WHERE book_name = 'mysql';
#⑥ 创建全文索引 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;
#第2种:表已经创建成功 #① ALTER TABLE ... ADD ... CREATE TABLE book5( book_id INT , book_name VARCHAR(100), AUTHORS VARCHAR(100), info VARCHAR(100) , COMMENT VARCHAR(100), year_publication YEAR );
SHOW INDEX FROM book5;
ALTER TABLE book5 ADD INDEX idx_cmt(COMMENT);#创建普通索引
ALTER TABLE book5 ADD UNIQUE uk_idx_bname(book_name);#创建唯一性索引
ALTER TABLE book5 ADD INDEX mul_bid_bname_info(book_id,book_name,info);#创建联合索引
#② CREATE INDEX ... ON ... CREATE TABLE book6( book_id INT , book_name VARCHAR(100), AUTHORS VARCHAR(100), info VARCHAR(100) , COMMENT VARCHAR(100), year_publication YEAR );
SHOW INDEX FROM book6;
CREATE INDEX idx_cmt ON book6(COMMENT);
CREATE UNIQUE INDEX uk_idx_bname ON book6(book_name);
CREATE INDEX mul_bid_bname_info ON book6(book_id,book_name,info);
DELIMITER // CREATE PROCEDURE ts_insert() BEGIN DECLARE i INT DEFAULT 1; WHILE i < 800 DO insert into ts1 select rand()*80000,rand()*80000; SET i = i + 1; END WHILE; commit; END // DELIMITER ; #调用 CALL ts_insert();
在MySQL 5.7版本中查看数据表ts1的执行计划,结果如下:
1
EXPLAIN SELECT * FROM ts1 ORDER BY a,b DESC LIMIT 5;
DELIMITER // CREATE PROCEDURE ts_insert() BEGIN DECLARE i INT DEFAULT 1; WHILE i < 800 DO INSERT INTO ts1 SELECT RAND()*80000,RAND()*80000; SET i = i + 1; END WHILE; COMMIT; END // DELIMITER ;
#调用 CALL ts_insert();
SELECT COUNT(*) FROM ts1; #优化测试 EXPLAIN SELECT * FROM ts1 ORDER BY a,b DESC LIMIT 5;
#不推荐 EXPLAIN SELECT * FROM ts1 ORDER BY a DESC,b DESC LIMIT 5;
#2. 隐藏索引 #① 创建表时,隐藏索引 CREATE TABLE book7( book_id INT , book_name VARCHAR(100), AUTHORS VARCHAR(100), info VARCHAR(100) , COMMENT VARCHAR(100), year_publication YEAR, #创建不可见的索引 INDEX idx_cmt(COMMENT) invisible );
SHOW INDEX FROM book7;
EXPLAIN SELECT * FROM book7 WHERE COMMENT = 'mysql....';
#② 创建表以后 ALTER TABLE book7 ADD UNIQUE INDEX uk_idx_bname(book_name) invisible;
CREATE INDEX idx_year_pub ON book7(year_publication);
EXPLAIN SELECT * FROM book7 WHERE year_publication = '2022';
#修改索引的可见性 ALTER TABLE book7 ALTER INDEX idx_year_pub invisible; #可见--->不可见
ALTER TABLE book7 ALTER INDEX idx_cmt visible; #不可见 ---> 可见
#了解:使隐藏索引对查询优化器可见
SELECT @@optimizer_switch \G
SET SESSION optimizer_switch="use_invisible_indexes=on";
EXPLAIN SELECT * FROM book7 WHERE year_publication = '2022';
#函数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 ;
#函数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 ;
某个字段在SELECT语句的 WHERE 条件中经常被使用到,那么就需要给这个字段创建索引了。尤其是在数据量大的情况下,创建普通索引就可以大幅提升数据查询的效率。 比如student_info数据表(含100万条数据),假设我们想要查询 student_id=123110 的用户信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#② 频繁作为 WHERE 查询条件的字段 #查看当前stduent_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; #276ms
#给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; #43ms #将作为where查询条件的字段student_id设为索引后查询效率提高了
3.经常 GROUP BY 和 ORDER BY 的列
索引就是让数据按照某种顺序进行存储或检索,因此当我们使用 GROUP BY 对数据进行分组查询,或者使用 ORDER BY 对数据进行排序的时候,就需要 对分组或者排序的字段进行索引 。如果待排序的列有多个,那么可以在这些列上建立 组合索引 比如,按照student_id对学生选修的课程进行分组,显示不同的student_id和课程数目,显示100个:
1 2 3 4 5 6 7 8 9 10 11 12 13
#③ 经常 GROUP BY 和 ORDER BY 的列 #student_id字段上有索引的: SELECT student_id, COUNT(*) AS num FROM student_info GROUP BY student_id LIMIT 100; #41ms
#删除idx_sid索引 DROP INDEX idx_sid ON student_info;
#student_id字段上没有索引的: SELECT student_id, COUNT(*) AS num FROM student_info GROUP BY student_id LIMIT 100; #866ms
首先, 连接表的数量尽量不要超过 3 张 ,因为每增加一张表就相当于增加了一次嵌套的循环,数量级增长会非常快,严重影响查询的效率。 其次, 对 WHERE 条件创建索引 ,因为 WHERE 才是对数据条件的过滤。如果在数据量非常大的情况下,没有 WHERE 条件过滤是非常可怕的。 最后, 对用于连接的字段创建索引 ,并且该字段在多张表中的 类型必须一致 。比如 course_id 在student_info 表和 course 表中都为 int(11) 类型,而不能一个为 int 另一个为 varchar 类型。 举个例子,如果我们只对 student_id 创建索引,执行 SQL 语句:
1 2 3 4
SELECT 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';
#其次,`对 WHERE 条件创建索引`,因为 WHERE 才是对数据条件的过滤。如果在数据量非常大的情况下,没有 WHERE 条件过滤是非常可怕的。
#最后,`对用于连接的字段创建索引`,并且该字段在多张表中的`类型必须一致`。比如 course_id 在 student_info 表和 course 表中都为 int(11) 类型,而不能一个为 int 另一个为 varchar 类型。 SELECT s.course_id, NAME, s.student_id, c.course_name FROM student_info s JOIN course c ON s.course_id = c.course_id WHERE NAME = '462eed7ac6e791292a79'; #0.001s
DROP INDEX idx_name ON student_info;
SELECT s.course_id, NAME, s.student_id, c.course_name FROM student_info s JOIN course c ON s.course_id = c.course_id WHERE NAME = '462eed7ac6e791292a79'; #0.227s
CREATE TABLE t_without_index( a INT PRIMARY KEY AUTO_INCREMENT, b INT );
提供存储过程1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#创建存储过程 DELIMITER // CREATE PROCEDURE t_wout_insert() BEGIN DECLARE i INT DEFAULT 1; WHILE i <= 900 DO INSERT INTO t_without_index(b) SELECT RAND()*10000; SET i = i + 1; END WHILE; COMMIT; END // DELIMITER ; #调用 CALL t_wout_insert();
创建表2:
1 2 3 4 5
CREATE TABLE t_with_index( a INT PRIMARY KEY AUTO_INCREMENT, b INT, INDEX idx_b(b) );
创建存储过程2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#创建存储过程 DELIMITER // CREATE PROCEDURE t_with_insert() BEGIN DECLARE i INT DEFAULT 1; WHILE i <= 900 DO INSERT INTO t_with_index(b) SELECT RAND()*10000; SET i = i + 1; END WHILE; COMMIT; END // DELIMITER ; #调用 CALL t_with_insert();
查询对比:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
select * from t_without_index where b = 9879; /* +------+------+ | a | b | +------+------+ | 1242 | 9879 | +------+------+ */ select * from t_with_index where b = 9879; /* +-----+------+ | a | b | +-----+------+ | 112 | 9879 | +-----+------+ */
CREATE TABLE person_info( id INT UNSIGNED NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, birthday DATE NOT NULL, phone_number CHAR(11) NOT NULL, country varchar(100) NOT NULL, PRIMARY KEY (id), KEY idx_name_birthday_phone_number (name(10), birthday, phone_number), KEY idx_name (name(10)) );
我们知道,通过idx_name_birthday_phone_number 索引就可以对 name 列进行快速搜索,再创建一个专门针对 name 列的索引就算是一个 冗余索引 ,维护这个索引只会增加维护的成本,并不会对搜索有什么好处。 ② 重复索引 另一种情况,我们可能会对某个列 重复建立索引 ,比方说这样:
1 2 3 4 5 6
CREATE TABLE repeat_index_demo ( col1 INT PRIMARY KEY, col2 INT, UNIQUE uk_idx_c1 (col1), INDEX idx_c1 (col1) );