MySQL索引和外键
索引介绍
索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度。
优点:加快搜索速度,减少查询时间 。
-
缺点:
索引是以文件存储的。如果索引过多,占磁盘空间较大。而且他影响: insert ,update ,delete 执行时间。
索引中数据必须与数据表数据同步:如果索引过多,当表中数据更新的时候,索引也要同步更新,这就降低了insert、update、delete的效率。
索引的类型
- 普通索引 (MUL) 加快我们的查询速度
- 唯一性索引 (UNI) 加快我们的查询速度、字段不可以插入重复的值,允许有null值
- 主键索引 (PRI) 加快查询速度,字段不可以插入重复的值,不允许有null值,每个表只允许有一个主键索引。
- 复合索引 (PRI) 加快查询速度,字段不可以插入重负的值,多个字段设置一个索引。
普通索引 (MUL)
创建索引语法
mysql> create table student1(id int,name varchar(10),age tinyint,index (id));
Query OK, 0 rows affected (0.00 sec)
mysql> desc student1;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | YES | MUL | NULL | |
| name | varchar(10) | YES | | NULL | |
| age | tinyint(4) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
给表添加索引
mysql> alter table student add index (id);
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | YES | MUL | NULL | |
| name | varchar(10) | YES | | NULL | |
| age | tinyint(4) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
查看索引
mysql> show index from student;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| student | 1 | id | 1 | id | A | 0 | NULL | NULL | YES | BTREE | | |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)
删除索引
mysql> alter table student drop key ids;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from student;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| student | 1 | name | 1 | name | A | 0 | NULL | NULL | YES | BTREE | | |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)
唯一性索引 (UNI)
添加索引语法
mysql> alter table student add unique key ids (id);
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | YES | UNI | NULL | |
| name | varchar(10) | YES | MUL | NULL | |
| age | tinyint(4) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
可以插入空值和null,不能插入重复值
mysql> insert into student values(1,"sss",12);
Query OK, 1 row affected (0.00 sec)
mysql> insert into student values(1,"sss",12);
ERROR 1062 (23000): Duplicate entry '1' for key 'ids'
mysql> insert into student values(2,"",12);
Query OK, 1 row affected (0.00 sec)
mysql> select * from student;
+------+------+------+
| id | name | age |
+------+------+------+
| 1 | sss | 12 |
| 2 | | 12 |
+------+------+------+
2 rows in set (0.00 sec)
mysql> insert into student(name,age) values(2,12);
Query OK, 1 row affected (0.00 sec)
mysql> select * from student;
+------+------+------+
| id | name | age |
+------+------+------+
| 1 | sss | 12 |
| 2 | | 12 |
| NULL | 2 | 12 |
+------+------+------+
3 rows in set (0.00 sec)
主键索引 (PRI)
主键索引要求列的所有内容必须唯一。主键索引是一种特殊的唯一索引, 但不允许有null值。每个表最多只能有一个主键。
create table 表名(
列定义,
primary key 索引名1(字段),
primary key 索引名2(字段)
);
mysql> alter table student add primary key ages (age);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | YES | UNI | NULL | |
| name | varchar(10) | YES | MUL | NULL | |
| age | tinyint(4) | NO | PRI | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
复合主键(PRI)
CREATE TABLE 表名 (
列定义,
PRIMARY KEY 索引名1 (字段1, 字段2)
);
(字段1, 字段2)
表示这两个字段(字段1
和 字段2
)是主键的一部分,它们联合在一起构成主键索引。这就是 复合主键(Composite Primary Key)。
案例:
mysql> ALTER TABLE student add primary key(id,name);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from student;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| student | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | |
| student | 0 | PRIMARY | 2 | name | A | 0 | NULL | NULL | | BTREE | | |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.00 sec)
mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | varchar(10) | NO | PRI | NULL | |
| age | tinyint(4) | NO | | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
主键要求唯一性:复合主键((字段1, 字段2)
)要求这两个字段组合的值是唯一的,不能有重复的组合。
字段顺序重要:在复合主键中,字段的顺序是非常重要的。索引会按字段顺序排列,因此查询时如果使用了主键列的前部分列,会更有效率。例如,在上面的例子中,查询 id
和 name
组合时会使用复合主键索引,但如果只查询 name
则无法利用这个索引
场景 1:查询 (id, name)
组合
如果你查询的是 完整的复合主键,即同时包含 id
和 name
,索引能够完全被利用。
场景 2:只查询 id
字段
如果查询中只涉及 主键的前部分列,例如查询 id
,那么 MySQL 仍然能够使用复合索引。
场景 3:只查询 name
字段
如果查询只涉及复合主键的 后部分字段,例如仅查询 name
,则复合索引无法被完全有效利用。
有效查询:如果查询条件包含复合主键的 前部分字段,MySQL 能有效利用索引加速查询。
无效查询:如果查询条件只涉及复合主键的 后部分字段,MySQL 无法有效利用复合索引,这可能导致性能下降。
字段修饰符
-
binary 修饰符: 区分字符大小写 也可以在建表的时候直接使用
-
NULL和NOT NULL
-
DEFAULT设置默认字段
-
auto_increment字段约束
AUTO_INCREMENT自动增长,只能修饰 int字段。表明mysql应该自动为该字段生成一个唯一没有用过的数,每次在最大ID值的基础上加1。比如:如果目前最大ID是34,然后删除34,新添加的会是35。需要配合主键或者唯一索引使用。
binary
使用场景
- 避免字符集和排序规则的影响:如果你需要确保字段值的比较不受字符集和排序规则的影响,可以使用
BINARY
修饰符。 - 存储二进制数据:如果你要存储的是二进制数据(比如 MD5 哈希值、加密数据等),也可以使用
BINARY
来确保每个字节都被直接存储和比较。
区分大小写:字符的大小写会被视为不同的字符。例如,'a'
和 'A'
会被认为是不同的字符。
按字节比较:MySQL 使用字段的字节表示来进行比较,而不是将字符映射为其对应的字符集编码。这适用于所有的字符比较、排序和搜索操作。
创建表时使用 BINARY
:
CREATE TABLE users (
id INT PRIMARY KEY,
username VARCHAR(100) BINARY
);
mysql> create table user (id int primary key, username varchar(7) binary);
Query OK, 0 rows affected (0.00 sec)
mysql> desc user;
+----------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| username | varchar(7) | YES | | NULL | |
+----------+------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
BINARY
作为列的修饰符,通常在 DESC
结果中不会显示在 Extra
列中,而是包含在 Type
字段中。如果你需要确保其生效,可以通过实际的查询和操作来验证。
mysql> SELECT * FROM user WHERE username = 'Alice';
+----+----------+
| id | username |
+----+----------+
| 1 | Alice |
+----+----------+
1 row in set (0.00 sec)
mysql> SELECT * FROM user WHERE username = 'alice';
+----+----------+
| id | username |
+----+----------+
| 2 | alice |
+----+----------+
1 row in set (0.00 sec)
NULL和NOT NULL
在 MySQL 中,NULL
和 NOT NULL
是用来控制数据库表中字段是否可以存储 NULL
值的修饰符。它们在定义表结构时非常重要,决定了列的约束和数据的完整性
1. NULL
NULL
表示该字段可以存储空值,即 没有值。在 SQL 中,NULL
并不等同于空字符串或零,它代表字段没有任何数据,通常用于表示数据丢失或不适用的情况。
- 默认行为:如果你没有显式指定字段是否为
NOT NULL
,MySQL 会默认允许该字段为NULL
。即使你没有指定NULL
,它也会被认为是允许的。 - 存储和查询:
NULL
不等同于空字符串''
或零0
。你需要使用专门的IS NULL
或IS NOT NULL
来检查字段是否为NULL
,不能使用常规的=
或<>
运算符。
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(100) NULL
);
可以插入空值和null
mysql> insert into user2(id) values(2);
Query OK, 1 row affected (0.01 sec)
mysql> select * from user2;
+----+----------+
| id | username |
+----+----------+
| 2 | NULL |
+----+----------+
1 row in set (0.01 sec)
mysql> insert into user2(id,username) values(3,"");
Query OK, 1 row affected (0.01 sec)
mysql> select * from user2
-> ;
+----+----------+
| id | username |
+----+----------+
| 2 | NULL |
| 3 | |
+----+----------+
2 rows in set (0.00 sec)
2.NOT NULL
NOT NULL
强制该字段 不能为空,也就是说,插入或更新数据时,必须为该字段提供有效的值。字段不能包含 NULL
,否则会引发错误。
- 约束:当你创建或修改表结构时,如果字段定义为
NOT NULL
,那么每次插入或更新记录时,必须为该字段提供一个值。 - 常见用途:
NOT NULL
用于那些在业务逻辑中必须有值的字段,比如用户的email
地址、phone_number
、age
等。
示例
CREATE TABLE users (
id INT PRIMARY KEY,
email VARCHAR(255) NOT NULL
);
mysql> alter table user2 modify username varchar(7) not null;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc user2;
+----------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| username | varchar(7) | NO | | NULL | |
+----------+------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
不允许插入null值,可以插入空值
mysql> insert into user2(id) values(1);
ERROR 1364 (HY000): Field 'username' doesn't have a default value
mysql> insert into user2 values(1,"");
Query OK, 1 row affected (0.01 sec)
mysql> select * from user2;
+----+----------+
| id | username |
+----+----------+
| 1 | |
+----+----------+
1 row in set (0.00 sec)
修改列的字段
如果你希望修改已有列的字段修饰符(比如修改列的数据类型、长度、NOT NULL
约束等),可以使用 ALTER TABLE
语句中的 MODIFY COLUMN
。
ALTER TABLE table_name MODIFY COLUMN column_name new_column_definition;
table_name
:要修改列的表的名称。
column_name
:要修改的列的名称。
new_column_definition
:新的列定义,包括数据类型、长度、NOT NULL
、默认值等。
mysql> alter table user2 modify column username varchar(7) null;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
修改字段的时候必须加上数据类型
DEFAULT
在 MySQL 中,可以使用 DEFAULT
关键字为列设置默认值。这意味着当插入新记录时,如果没有为某个列提供值,那么 MySQL 会使用该列的默认值。
语法
ALTER TABLE table_name MODIFY COLUMN column_name column_definition DEFAULT default_value;
table_name
:表的名称。column_name
:要设置默认值的列名。column_definition
:列的数据类型及其他属性(如NOT NULL
、AUTO_INCREMENT
等)。default_value
:默认值。可以是一个常量值,也可以是NULL
,具体取决于列的定义。
示例:给列设置默认值
假设我们有一个表 user2
,该表包含列 username
和 age
,我们想为 username
设置默认值 'guest'
,并为 age
设置默认值 18
。
假设 username
列的数据类型是 VARCHAR(50)
,可以使用以下语句设置默认值:
ALTER TABLE user2 MODIFY COLUMN username VARCHAR(50) DEFAULT 'guest';
这条语句将为 username
列设置默认值 'guest'
。当插入新记录时,如果没有指定 username
的值,那么该列将自动填充为 'guest'
2. 为 INT
类型的列设置默认值
如果 age
列的数据类型是 INT
,您可以为其设置默认值 18
:
ALTER TABLE user2 MODIFY COLUMN age INT DEFAULT 18;
这将为 age
列设置默认值为 18
。插入数据时,如果没有提供 age
的值,它将自动使用默认值 18
。
同时修改age和username的default值
mysql> alter table user2 modify column age int default 18,modify column username varchar(7) default "guest";
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc user2;
+----------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| username | varchar(7) | YES | | guest | |
| age | int(11) | YES | | 18 | |
+----------+------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
测试
mysql> insert into user2(id) values(1);
Query OK, 1 row affected (0.00 sec)
mysql> select * from user2;
+----+----------+------+
| id | username | age |
+----+----------+------+
| 1 | guest | 18 |
+----+----------+------+
1 row in set (0.00 sec)
3. 使用 DEFAULT
设置 NULL
如果您希望列的默认值是 NULL
(例如,当没有为列提供值时允许为空),您可以显式地设置默认值为 NULL
:
ALTER TABLE user2 MODIFY COLUMN age INT DEFAULT NULL;
这将使 age
列的默认值为 NULL
,即如果插入数据时没有提供该列的值,age
将自动设置为 NULL
。
4. 添加新列并设置默认值
您还可以在 ALTER TABLE
中添加新列并设置默认值:
ALTER TABLE user2 ADD COLUMN last_login DATETIME DEFAULT CURRENT_TIMESTAMP;
这会向 user2
表添加一个新列 last_login
,并将其默认值设置为当前时间戳。
例子
mysql> ALTER TABLE user2 ADD COLUMN last_login DATETIME DEFAULT CURRENT_TIMESTAMP;
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> insert into user2(id) values(3);
Query OK, 1 row affected (0.01 sec)
mysql>
mysql>
mysql> select * from user2;
+----+----------+------+---------------------+
| id | username | age | last_login |
+----+----------+------+---------------------+
| 1 | guest | 18 | 2024-12-16 13:52:40 |
| 2 | guest | NULL | 2024-12-16 13:52:40 |
| 3 | guest | NULL | 2024-12-16 13:52:45 |
+----+----------+------+---------------------+
3 rows in set (0.00 sec)
AUTO_INCREMENT
在 MySQL 中,AUTO_INCREMENT
是一种特殊的列属性,通常用于主键字段,用来自动生成唯一的递增数字值。它在插入新记录时,自动为该字段生成一个唯一的值,通常从 1 开始递增。
使用 AUTO_INCREMENT
字段的基本规则和约束:
1. AUTO_INCREMENT
的适用条件
AUTO_INCREMENT
列通常用作表的主键或唯一索引的列。- 该列必须是 数字类型,如
INT
、BIGINT
、SMALLINT
、TINYINT
等。 - 该列不能显式地插入值,除非需要手动覆盖生成的值(通过
INSERT
时提供具体值)。 - 只能有一个
AUTO_INCREMENT
列:每张表最多只能有一个AUTO_INCREMENT
列。
-
创建表时使用
AUTO_INCREMENT
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50),
email VARCHAR(100)
);
3. 修改表时添加 AUTO_INCREMENT
如果需要为已经存在的表添加一个 AUTO_INCREMENT
字段,可以使用 ALTER TABLE
语句。
ALTER TABLE users ADD COLUMN id INT AUTO_INCREMENT PRIMARY KEY;
这会向 users
表中添加一个新的 id
列,并将其设置为自动递增的主键。
-
AUTO_INCREMENT
的默认行为
- 默认情况下,
AUTO_INCREMENT
列的值从 1 开始。 - 如果需要,可以通过
AUTO_INCREMENT
选项来设置初始值。 AUTO_INCREMENT
会在每次插入新行时递增,递增的步长默认为 1。
- 设置
AUTO_INCREMENT
起始值
在创建表时,可以使用 AUTO_INCREMENT
设置列的起始值:
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50)
) AUTO_INCREMENT = 1000;
这表示 id
列将从 1000 开始递增。
你也可以通过 ALTER TABLE
来修改 AUTO_INCREMENT
的起始值:
ALTER TABLE users AUTO_INCREMENT = 1000;
这会将 users
表的 AUTO_INCREMENT
值设置为 1000,下一条插入的记录将使用 1000 作为 id
值。
7. 删除 AUTO_INCREMENT
约束
如果你需要移除列的 AUTO_INCREMENT
约束,可以通过 ALTER TABLE
来做到这一点:
ALTER TABLE users MODIFY COLUMN id INT;
这会删除 id
列的 AUTO_INCREMENT
属性,使其不再自动递增。
外键
MySQL中的外键(Foreign Key)是一种约束,用于确保数据库中不同表之间的数据一致性和完整性。外键的主要作用是建立两个表之间的关系,确保子表中的数据只能引用父表中存在的数据。
外键的基本概念
- 父表(Parent Table):外键约束指向的表,通常是存储主键(Primary Key)或唯一键(Unique Key)的表。
- 子表(Child Table):包含外键字段的表,外键字段通常是指向父表的某个列。
- 外键字段(Foreign Key Field):子表中用来引用父表主键或唯一键的字段。
举例说明
假设我们有两个表:orders
(订单表)和 customers
(客户表)。
customers
表的主键是customer_id
。orders
表中的customer_id
列是外键,引用customers
表中的customer_id
列。
CREATE TABLE customers (
customer_id INT PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE orders (
order_id INT AUTO_INCREMENT PRIMARY KEY,
order_date DATE,
customer_id INT,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
ON DELETE CASCADE
);
在这个例子中:
customer_id
是orders
表中的外键列,引用customers
表中的customer_id
列。customer_id
列在orders
表中确保了每一条订单记录都有一个有效的客户 ID,并且该 ID 必须存在于customers
表中。
ON DELETE CASCADE 级联删除
ON UPDATE CASCADE 级联更新
- 创建外键注意事项:
- 外键是在从表上创建的
- 确保主表和主表被关联字段必须存在
- 主表被关联的字段必须设置为主键
- 主表被关联字段和从表关联字段数据类型必须一致
- 表必须使用innodb存储引擎