MySQL 实战:从建表到索引管理的完整指南

目录

  • [1. 引言](#1. 引言)
  • [2. 创建数据库与选择数据库](#2. 创建数据库与选择数据库)
  • [3. 创建学生信息表](#3. 创建学生信息表)
  • [4. 查看表结构信息](#4. 查看表结构信息)
  • [5. 向表中插入数据](#5. 向表中插入数据)
  • [6. 修改表结构(ALTER TABLE)](#6. 修改表结构(ALTER TABLE))
  • [7. 索引(Index)详解](#7. 索引(Index)详解)
    • [7.1 索引是什么](#7.1 索引是什么)
    • [7.2 索引的优缺点](#7.2 索引的优缺点)
    • [7.3 InnoDB 常见索引分类](#7.3 InnoDB 常见索引分类)
    • [7.4 基础语法示例](#7.4 基础语法示例)
    • [7.5 索引使用示例](#7.5 索引使用示例)
    • [7.6 索引使用注意事项](#7.6 索引使用注意事项)
  • [8. 总结](#8. 总结)## 1. 引言

在数据库学习和开发中,掌握从基础表操作到高级性能优化(如索引)的完整 SQL 技能至关重要。本文将通过一个连贯的实战案例,手把手演示如何使用 MySQL 完成数据库创建、数据表管理、数据插入、表结构修改,并深入讲解索引的创建、管理与核心原理。我们将创建一个名为 abc 的数据库,并在其中管理一个学生信息表 s1,通过具体的 SQL 语句讲解每个操作步骤及其背后的设计思考。

2. 创建数据库与选择数据库

首先,我们需要创建一个新的数据库来存放我们的数据表。

sql 复制代码
-- 创建数据库
CREATE DATABASE abc;

执行上述语句后,MySQL 会创建一个名为 abc 的数据库。接下来,我们需要告诉 MySQL 后续的操作都在这个数据库中进行。

sql 复制代码
-- 使用数据库
USE abc;

USE 语句用于选择当前要操作的数据库。执行后,所有未指定数据库的表操作都将默认在 abc 数据库中进行。

3. 创建学生信息表

在操作表之前,一个好的习惯是确保目标表不存在,避免因重复创建而报错。

sql 复制代码
-- 删除数据表(如果存在)
DROP TABLE IF EXISTS s1;

DROP TABLE IF EXISTS 是一个安全的做法,它只在表存在时才执行删除操作。

现在,我们来创建核心的学生信息表 s1

sql 复制代码
-- 创建学生表
CREATE TABLE s1 (
   id INT PRIMARY KEY AUTO_INCREMENT COMMENT '编号',
   s_name VARCHAR(20) UNIQUE NOT NULL COMMENT '姓名',
   age INT NOT NULL COMMENT '年龄',
   sex ENUM('男', '女', '未知') DEFAULT '未知' COMMENT '性别',
   s_sno CHAR(12) UNIQUE NOT NULL COMMENT '学号',
   tel CHAR(11) NOT NULL COMMENT '手机号',
   start_time DATE DEFAULT (CURRENT_DATE) COMMENT '入学时间',
   status ENUM('在读', '毕业') DEFAULT '在读' COMMENT '状态'
);

表结构说明:

  • id: 主键,自动增长,作为每条记录的唯一标识。
  • s_name: 学生姓名,VARCHAR(20) 类型,UNIQUE 约束确保姓名不重复,NOT NULL 表示必填。
  • age: 年龄,整型,必填。
  • sex: 性别,使用 ENUM 类型限定为'男'、'女'、'未知'三种值,默认值为'未知'。
  • s_sno: 学号,定长字符串,唯一且必填。
  • tel: 手机号,定长字符串,必填。
  • start_time: 入学时间,日期类型,默认值为系统当前日期。
  • status: 状态,ENUM 类型,表示'在读'或'毕业',默认'在读'。

4. 查看表结构信息

创建表后,我们可以通过多种方式查看其结构。

1. 查看表的基本字段信息(DESC)

sql 复制代码
-- 查看表结构
DESC s1;

DESC (或 DESCRIBE) 命令会以表格形式列出表的所有字段名、类型、是否为空、键信息、默认值和额外说明。

2. 查看字段信息(SHOW COLUMNS)

sql 复制代码
-- 查看字段
SHOW COLUMNS FROM s1;

SHOW COLUMNSDESC 功能类似,提供了另一种查看表结构的方式。

3. 查看完整的建表语句

sql 复制代码
-- 查看创建表语句
SHOW CREATE TABLE s1;

这条命令会返回创建该表的完整 SQL 语句,包括引擎、字符集等所有细节,对于备份或迁移表结构非常有用。

5. 向表中插入数据

表创建好后,我们需要向其中添加数据。以下是插入8条学生记录的示例:

sql 复制代码
-- 插入数据
INSERT INTO s1 VALUES (NULL, '张三', 18, '男', '2019001', '13888888888', '2020-09-01', '在读');
INSERT INTO s1 VALUES (NULL, '李四', 19, '女', '2019002', '13888888889', '2020-09-01', '在读');
INSERT INTO s1 VALUES (NULL, '王五', 20, '未知', '2019003', '13888888890', '2020-09-01', '在读');
INSERT INTO s1 VALUES (NULL, '赵六', 21, '男', '2019004', '13888888891', '2020-09-01', '在读');
INSERT INTO s1 VALUES (NULL, '孙七', 22, '女', '2019005', '13888888892', '2020-09-01', '在读');
INSERT INTO s1 VALUES (NULL, '周八', 23, '未知', '2019006', '13888888893', '2020-09-01', '在读');
INSERT INTO s1 VALUES (NULL, '吴九', 24, '男', '2019007', '13888888894', '2020-09-01', '在读');
INSERT INTO s1 VALUES (NULL, '郑十', 25, '女', '2019008', '13888888895', '2020-09-01', '在读');

注意:id 字段传入了 NULL,由于其是 AUTO_INCREMENT,数据库会自动为其生成递增值。

6. 修改表结构(ALTER TABLE)

在实际应用中,表结构可能需要调整。ALTER TABLE 语句用于添加、修改或删除表中的列。

1. 添加新字段

假设我们需要为学生表增加一个"身份证号"字段。

sql 复制代码
-- 添加身份证号字段
ALTER TABLE s1 ADD COLUMN id_card CHAR(18) COMMENT '身份证号';

2. 删除字段

如果添加的字段不再需要,可以将其删除。

sql 复制代码
-- 删除身份证号字段
ALTER TABLE s1 DROP COLUMN id_card;

7. 索引(Index)详解

7.1 索引是什么

索引是建立在数据表字段上的数据结构(MySQL InnoDB 默认使用 B+Tree),相当于书本的目录。

作用 :快速定位数据,减少全表扫描,极大提升查询(SELECT)语句的速度

7.2 索引的优缺点

优点:

  1. 极大加快数据查询速度 :特别是在 WHERE 条件筛选、ORDER BY 排序、GROUP BY 分组时效率提升显著。

缺点:

  1. 占用额外磁盘存储空间:索引本身需要存储。
  2. 降低 DML 性能 :进行 INSERTUPDATEDELETE 操作时,数据库需要额外维护索引结构,可能导致写入变慢。
  3. 索引不是越多越好:无用的索引只会增加存储与写入开销,需要根据查询需求合理设计。

7.3 InnoDB 常见索引分类

  1. 主键索引 (PRIMARY)

    • 特点:唯一、非空;一张表只能有一个主键。
    • InnoDB 特性 :InnoDB 存储引擎中,主键索引即聚簇索引,表数据本身按主键顺序存储在主键索引的叶子节点上。
  2. 唯一索引 (UNIQUE)

    • 特点 :索引列的值不能重复,但允许有一个 NULL 值;一张表可以有多个唯一索引。
  3. 普通索引 (INDEX)

    • 特点 :最基础的索引类型,仅用于加速查询。允许重复值和 NULL 值。
  4. 联合索引(复合索引)

    • 特点:在多个字段组合上创建的索引。
    • 核心原则 :遵循最左前缀原则。查询条件必须包含联合索引的最左列,才能有效利用该索引。
  5. 全文索引 (FULLTEXT)

    • 特点 :专门用于对长文本内容(如 TEXT 类型字段)进行全文检索,可以高效替代低效的 LIKE '%keyword%' 模糊查询。

7.4 基础语法示例

以下示例基于已添加的 id_card 字段和已有的 s_nameage 字段。

1. 创建普通索引

sql 复制代码
-- 语法:CREATE INDEX 索引名 ON 表名(字段名);
CREATE INDEX name ON s1(s_name);

2. 创建联合索引

sql 复制代码
-- 语法:CREATE INDEX 索引名 ON 表名(字段名1, 字段名2);
CREATE INDEX name_age ON s1(s_name, age);

3. 创建全文索引

sql 复制代码
-- 语法:CREATE FULLTEXT INDEX 索引名 ON 表名(字段名);
CREATE FULLTEXT INDEX name_fulltext ON s1(s_name);

4. 创建唯一索引

sql 复制代码
-- 语法:CREATE UNIQUE INDEX 索引名 ON 表名(字段名);
CREATE UNIQUE INDEX idcard ON s1(id_card);

5. 查看索引

sql 复制代码
-- 语法:SHOW INDEX FROM 表名;
SHOW INDEX FROM s1;

该命令会显示表上所有索引的名称、类型、关联的字段、唯一性等信息。

6. 删除索引

sql 复制代码
-- 语法:DROP INDEX 索引名 ON 表名;
DROP INDEX idcard ON s1;

7.6 索引使用注意事项了解了索引的创建语法后,让我们通过具体的查询示例来展示索引如何提升性能。

示例1:普通索引加速查询

假设我们经常需要按学生姓名查询:

sql 复制代码
-- 无索引时的查询(全表扫描)
EXPLAIN SELECT * FROM s1 WHERE s_name = '张三';

-- 创建姓名索引后
CREATE INDEX idx_name ON s1(s_name);

-- 再次执行相同查询(使用索引)
EXPLAIN SELECT * FROM s1 WHERE s_name = '张三';

执行计划对比:

  • 无索引时:typeALL(全表扫描),rows 为表总行数
  • 有索引时:typeref(索引查找),rows 为1(精确匹配)
示例2:联合索引与最左前缀原则

创建联合索引后,观察不同查询条件的效果:

sql 复制代码
-- 创建联合索引
CREATE INDEX idx_name_age ON s1(s_name, age);

-- 情况1:使用索引(包含最左列s_name)
EXPLAIN SELECT * FROM s1 WHERE s_name = '李四' AND age = 19;

-- 情况2:使用索引(只使用最左列s_name)
EXPLAIN SELECT * FROM s1 WHERE s_name = '王五';

-- 情况3:索引失效(未包含最左列s_name)
EXPLAIN SELECT * FROM s1 WHERE age = 20;

-- 情况4:部分使用索引(s_name范围查询,age无法使用索引)
EXPLAIN SELECT * FROM s1 WHERE s_name LIKE '张%' AND age = 18;
示例3:唯一索引防止重复数据
sql 复制代码
-- 尝试插入重复学号(会失败)
INSERT INTO s1 VALUES (NULL, '测试', 20, '男', '2019001', '13800000000', '2020-09-01', '在读');
-- 错误:Duplicate entry '2019001' for key 's_sno'

-- 查看唯一约束错误信息
SHOW ERRORS;
示例4:索引对排序的优化
sql 复制代码
-- 无索引时的排序(可能使用文件排序)
EXPLAIN SELECT * FROM s1 ORDER BY s_name;

-- 创建索引后
CREATE INDEX idx_name ON s1(s_name);

-- 再次执行排序(可能使用索引排序)
EXPLAIN SELECT * FROM s1 ORDER BY s_name;
示例5:索引失效场景演示
sql 复制代码
-- 1. 在索引列上使用函数(索引失效)
EXPLAIN SELECT * FROM s1 WHERE LEFT(s_name, 1) = '张';

-- 2. 隐式类型转换(索引失效)
-- 假设tel字段是CHAR(11),但查询时使用数字
EXPLAIN SELECT * FROM s1 WHERE tel = 13888888888;

-- 3. 使用OR条件(如果OR的某一侧没有索引,可能导致全表扫描)
EXPLAIN SELECT * FROM s1 WHERE s_name = '张三' OR age = 18;

-- 4. 使用不等于(!=或<>)(可能无法使用索引)
EXPLAIN SELECT * FROM s1 WHERE s_name != '张三';
示例6:覆盖索引(Covering Index)
sql 复制代码
-- 创建覆盖索引
CREATE INDEX idx_covering ON s1(s_name, age, sex);

-- 查询只涉及索引列,无需回表
EXPLAIN SELECT s_name, age FROM s1 WHERE s_name = '赵六' AND age > 20;

-- Extra列显示"Using index"表示使用了覆盖索引
示例7:索引选择性测试
sql 复制代码
-- 查看字段值的分布情况(评估索引效果)
SELECT 
    COUNT(DISTINCT sex) as sex_distinct_count,
    COUNT(DISTINCT age) as age_distinct_count,
    COUNT(DISTINCT s_name) as name_distinct_count,
    COUNT(*) as total_rows
FROM s1;

-- 在区分度高的字段上建索引效果更好
-- sex只有3种值,区分度低
-- s_name有8种值(假设不重复),区分度高
示例8:多列索引顺序优化
sql 复制代码
-- 根据查询频率和选择性决定索引列顺序
-- 假设查询模式1:WHERE age > 20 AND sex = '男'
-- 假设查询模式2:WHERE sex = '女' AND age > 18

-- 创建测试索引
CREATE INDEX idx_age_sex ON s1(age, sex);
CREATE INDEX idx_sex_age ON s1(sex, age);

-- 分别测试两个查询
EXPLAIN SELECT * FROM s1 WHERE age > 20 AND sex = '男';
EXPLAIN SELECT * FROM s1 WHERE sex = '女' AND age > 18;

-- 根据实际查询模式选择更优的索引顺序
示例9:索引维护操作
sql 复制代码
-- 1. 修改索引名称(MySQL 8.0+)
ALTER TABLE s1 RENAME INDEX idx_name TO idx_student_name;

-- 2. 禁用/启用索引(某些存储引擎支持)
-- ALTER TABLE s1 DISABLE KEYS;
-- ALTER TABLE s1 ENABLE KEYS;

-- 3. 分析索引使用情况
ANALYZE TABLE s1;

-- 4. 检查索引碎片
SHOW TABLE STATUS LIKE 's1';
-- 关注Data_free列,值较大时考虑优化表
OPTIMIZE TABLE s1;
示例10:实际性能对比测试
sql 复制代码
-- 准备测试数据(插入更多数据以观察性能差异)
DELIMITER $$
CREATE PROCEDURE insert_test_data()
BEGIN
    DECLARE i INT DEFAULT 1;
    WHILE i <= 10000 DO
        INSERT INTO s1 VALUES (
            NULL,
            CONCAT('测试学生', i),
            FLOOR(18 + RAND() * 10),
            ELT(FLOOR(1 + RAND() * 3), '男', '女', '未知'),
            CONCAT('202400', LPAD(i, 4, '0')),
            CONCAT('138', LPAD(FLOOR(RAND() * 100000000), 8, '0')),
            DATE_SUB(CURRENT_DATE, INTERVAL FLOOR(RAND() * 365) DAY),
            ELT(FLOOR(1 + RAND() * 2), '在读', '毕业')
        );
        SET i = i + 1;
    END WHILE;
END$$
DELIMITER ;

-- 执行存储过程插入测试数据
CALL insert_test_data();

-- 删除无索引时的查询测试
SET profiling = 1;
SELECT * FROM s1 WHERE s_name LIKE '测试学生%' ORDER BY age DESC LIMIT 100;
SHOW PROFILES;

-- 创建索引后的查询测试
CREATE INDEX idx_test_name ON s1(s_name);
SELECT * FROM s1 WHERE s_name LIKE '测试学生%' ORDER BY age DESC LIMIT 100;
SHOW PROFILES;

-- 对比查询时间
SET profiling = 0;

性能优化建议:

  1. 使用 EXPLAIN 分析查询执行计划
  2. 关注 type 列:const > ref > range > index > ALL
  3. 关注 Extra 列:避免 Using filesortUsing temporary
  4. 定期使用 ANALYZE TABLE 更新索引统计信息
  5. 监控慢查询日志,针对慢查询优化索引

7.5 索引使用注意事项

  1. 遵循最左前缀原则 :对于联合索引 (A, B, C),查询条件必须包含 A,索引才会生效。例如 WHERE A=1 AND B=2 有效,但 WHERE B=2 则无法使用该索引。
  2. 避免在索引列上使用函数或运算 :例如 WHERE YEAR(create_time) = 2023WHERE amount * 2 > 100 会导致索引失效。
  3. 注意隐式类型转换:例如字符串字段与数字比较时,可能导致索引失效。
  4. 选择区分度高的字段建立索引:在性别(只有'男'、'女')这种区分度很低的字段上建立索引,效果甚微。应优先在唯一性高或取值分布广泛的字段上建索引。
  5. 避免过度索引:每个额外的索引都会增加写操作的开销,定期审查并删除未使用的索引。
  6. 考虑索引大小:过长的索引字段(如TEXT类型)不适合建索引,可考虑前缀索引。
  7. 注意NULL值的影响:包含NULL值的索引列在查询时可能需要特殊处理。
  8. 定期维护索引 :使用 OPTIMIZE TABLEALTER TABLE ... REBUILD 整理索引碎片。## 8. 总结

本文通过一个从零开始的连贯案例,系统演示了 MySQL 数据库操作与性能优化的核心流程:

  1. 环境准备 :创建数据库 (CREATE DATABASE) 并切换使用 (USE)。
  2. 表管理 :安全删除旧表 (DROP TABLE IF EXISTS)、创建新表 (CREATE TABLE) 并查看其结构 (DESC, SHOW CREATE TABLE)。
  3. 数据操作 :向表中插入示例数据 (INSERT INTO)。
  4. 结构维护 :使用 ALTER TABLE 动态添加或删除字段,适应业务变化。
  5. 性能优化 :深入理解索引的原理、分类与优缺点,并实践了普通索引、联合索引、全文索引、唯一索引的创建 (CREATE INDEX)、查看 (SHOW INDEX) 和删除 (DROP INDEX),掌握了索引使用的关键注意事项。

掌握这些从基础到进阶的操作,是进行高效数据库设计、开发和优化的坚实基础。建议读者在 MySQL 客户端中亲自执行每一段 SQL 代码,并结合实际业务场景思考索引的设计,以加深理解。

相关推荐
宠友信息12 小时前
MySQL复合索引与Druid优化仿小红书源码个人主页查询链路
数据库·spring boot·websocket·mysql·uni-app
minji...12 小时前
MySQL数据库 (十九) MySQL图像化界面,MySQL连接池
数据库·mysql·navicat·mysql workbench·mysql连接池·数据库图形化界面
文档搬运工12 小时前
Innodb Cluster安装
mysql
想躺平的小羊12 小时前
MySQL中LAST_DAY函数用法
数据库·mysql
光影62713 小时前
MySQL基础入门
数据库·笔记·sql·学习·mysql·学习方法
qetfw13 小时前
CentOS 7 搭建 MariaDB 数据库服务
linux·数据库·centos·mariadb
光影62714 小时前
MySQL 进阶篇 —— 事务 / 外键 / 索引 / 高级查询
数据库·笔记·sql·学习·mysql
爬也要爬着前进14 小时前
redis哨兵模式搭建
数据库·redis·bootstrap
小月土星14 小时前
(实战篇)RAG-demo 深度解析:162行代码中的检索增强生成全景 (含Top-k法)
javascript·数据库·llm