13.2.6 索引

索引是存放在模式(schema)中的一个数据库对象,虽然索引总是从属于数据表,但它也和数据表一样属于数据库对象。创建索引的唯一作用就是加速对表的查询,索引通过使用快速路径访问方法来快速定位数据,从而减少了磁盘的I/O。
索引作为数据库对象,在数据字典中独立存放,但不能独立存在,必须属于某个表

提示

MySQL使用information_schema数据库里的STATISTICS表来保存该数据库实例中的所有索引信息,用户可通过查询该表来获取该数据库的索引信息。

创建索引

创建索引有两种方式。
自动:当在表上定义主键约束、唯一约束和外键约束时,系统会为该数据列自动创建对应的索引。
手动:用户可以通过create index...语句来创建索引。

删除索引

删除索引也有两种方式。
自动:数据表被删除时,该表上的索引自动被删除。
手动:用户可以通过drop index...语句来删除指定数据表上的指定索引。

一个表中可以有多个索引列,每个索引都可用于加速该列的查询速度

手动创建索引

通常为经常需要查询的数据列建立索引,可以在一列或者多列上创建索引。创建索引的语法格式如下:

1
2
create index 索引名称
on 表名 (column[,coulumn]...);

单列索引

下面的索引将会提高对employees表基于last_name字段的查询速度。

1
2
create index emp_last_name_idx
on employees(last_name);

多列索引

也可同时对多列建立索引,如下SQL语句所示:

1
2
create index emp_last_name_idx2
on employees(first_name,last_name);

删除索引

MySQL中删除索引需要指定表,采用如下语法格式:

1
drop index 索引名 on 表名

如下SQL语句删除了employees表上的emp_last_name_idx2索引:

1
2
drop index emp_last_name_idx2
on employees

有些数据库删除索引时无须指定表名,因为它们要求建立索引时每个索引都有唯一的名字,所以无须指定表名,例如Oracle就采用这种策略。但MySQL只要求同一个表内的索引不能同名,所以删除索引时必须指定表名。

索引优缺点

索引的好处是可以加速查询,但索引也有如下两个坏处。

  • 当数据表中的记录被添加、删除、修改时,数据库系统需要维护索引,因此有一定的系统开销。
  • 存储索引信息需要一定的磁盘空间。

13.2.5 数据库约束 5.CHECK约束

当前版本的MySQL支持建表时指定CHECK约束,但这个CHECK约束不会有任何作用。建立CHECK约束的语法很简单,只要在建表的列定义后增加check(逻辑表达式)即可。如下SQL语句所示:

1
2
3
4
5
6
7
8
create table check_test
(
emp_id int auto_increment,
emp_name varchar(255),
emp_salary key(emp_id),
primary key(emp_id),
check(emp_salary>0)
);

虽然上面的SQL语句建立的check_test表中有CHECK约束,CHECK约束要求emp_salary大于0,但这个要求实际上并不会起作用。

13.2.5 数据库约束 4.FOREIGN KEY约束

外键约束主要用于保证一个或两个数据表之间的参照完整性

从表

增加外键列的表被称为从表,只要为外键列增加唯一约束就可表示一对一的关联关系了。
外键是构建于一个表的两个字段或者两个表的两个字段之间的参照关系。外键确保了相关的两个字段的参照关系:子(从)表外键列的值必须在主表被参照列的值范围之内,或者为空(也可以通过非空约束来约束外键列不允许为空)。

当主表的记录被从表记录参照时,主表记录不允许被删除,必须先把从表里参照该记录的所有记录全部删除后,才可以删除主表的该记录还有一种方式,删除主表记录时级联删除从表中所有参照该记录的从表记录
从表外键参照的只能是主表主键列或者唯一键列,这样才可保证从表记录可以准确定位到被参照的主表记录。同一个表内可以拥有多个外键。
建立外键约束时,MySQL也会为该列建立索引。

如何确定外键设置的位置

外键约束通常用于定义两个实体之间的一对多、一对一的关联关系

一对多关联关系

对于一对多的关联关系,通常在多的一端增加外键列,
例如老师—学生(假设一个老师对应多个学生,但每个学生只有一个老师,这是典型的一对多的关联关系)。为了建立他们之间的关联关系,可以在学生表中增加一个外键列,该列中保存此条学生记录对应老师的主键

一对一关联关系

对于一对一的关联关系,则可选择任意一方来增加外键列

多对多联系

对于多对多的关联关系,则需要额外增加一个连接表来记录它们的关联关系。

外键约束写在哪里

建立外键约束同样可以采用列级约束语法和表级约束语法。如果仅对单独的数据列建立外键约束,则使用列级约束语法即可;如果需要对多列组合创建外键约束,或者需要为外键约束指定名字,则必须使用表级约束语法。

列级约束语法建立外键约束

采用列级约束语法建立外键约束直接使用references关键字,references指定该列参照哪个主表,以及参照主表的哪一列
语法格式如下:

1
references 主表(主表的列)

如下SQL语句所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
create table teacher_table
(
teacher_id int auto_increment,
teacher_name varchar(255),
primary key(teacher_id)
);
creater table student_table
(
student_id int auto_increment primary key,
student_name varchar(255),
# 这是一个外键,参照teacher_table表的teacher_id属性
java_teacher int references teacher_table(teacher_id)
);

MySQL中外键约束正确语法

值得指出的是,虽然MySQL支持使用列级约束语法来建立外键约束,但这种列级约束语法建立的外键约束不会生效MySQL提供这种列级约束语法仅仅是为了和标准SQL保持良好的兼容性。因此,如果要使MySQL中的外键约束生效,则应使用表级约束语法,也就是要写在表定义的后面,不要写在列定于中,如下所示:。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
create table teacher_table1
(
teacher_id int auto_increment,
teacher_name varchar(255),
primary key(teacher_id)
);
create table student_table
(
student_id int auto_increment primary key,
student_name varchar(255),
java_teacher int,
#指定java_teacher参照到teacher_table1的teacher_id列
foreign key(java_teacher)
referenses teacher_table1(teacher_id)
);

如果使用表级约束语法,则需要使用foreign key来指定本表的外键列,并使用references来指定参照哪个主表,以及参照到主表的哪个数据列
格式如下所示:

1
foreign key(本表中的列名) referenses 主表名(主表的列明)

使用表级约束语法可以为外键约束指定约束名,如果创建外键约束时没有指定约束名,则MySQL会为该外键约束命名为table_name_ibfk_n,其中table_name是从表的表名,而n是从1开始的整数。

指定外键约束的名字

如果需要显式指定外键约束的名字,则可使用constraint来指定名字。如下SQL语句所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
create table teacher_table2
(
teacher_id int auto_increment,
teacher_name varchar(255),
primary key(teacher_id)
);
create table student_table2
(
student_id int auto_increment primary key,
student_name varchar(255),
java_teacher int,
constraint student_teacher_fk foreign key(java_teacher)
references teacher_table2(teacher_id)
);

组合外键

如果需要建立多列组合的外键约束,则必须使用表级约束语法,如下SQL语句所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
create table teacher_table3
(
teacher_name varchar(255),
teacher_pass varchar(255),
# 以两个键作为主键
primary key(teacher_id,teacher_pass)
);
create table student_table3
(
student_id int auto_increment primary key,
student_name varchar(255),
java_teacher_name varchar(255),
java_teacher_pass varchar(255),
# 指定两列的联合外键
foreign key(java_teacher_name,java_teacher_pass)
referenses teacher_table3(teacher_name,teacher_pass)
);

删除外键约束

删除外键约束的语法很简单,在alter table后增加“drop foreign key 约束名”子句即可。如下代码所示:

1
2
alter table student_table3
drop foreign key student_table3_ibfk_i;

增加外键约束

增加外键约束通常使用add foreign key命令。如下SQL语句所示:

1
2
3
alter table student_table3
add foreign key(java_teacher_name,java_teacher_pass)
references teacher_table3(teacher_name,teacher_pass);

自关联

值得指出的是,外键约束不仅可以参照其他表,而且可以参照自身,这种参照自身的情况通常被称为自关联。例如,使用一个表保存某个公司的所有员工记录,员工之间有部门经理和普通员工之分,部门经理和普通员工之间存在一对多的关联关系,但他们都是保存在同一个数据表里的记录,这就是典型的自关联。下面的SQL语句用于建立自关联的外键约束。

1
2
3
4
5
6
7
8
9
create table foreign_test
(
foreign_id int auto_increment primary key,
foreign_name varchar(255),
#refer_id参照本表的foreign_id列
refer_id int,
foregin key(refer_id)
references foreign_test(foreign_id)
);

删除主表记录时从表记录也删除

如果想定义当删除主表记录时,从表记录也会随之删除,则需要在建立外键约束后添加on delete cascade或添加on delete set null,第一种是删除主表记录时,把参照该主表记录的从表记录全部级联删除;第二种是指定当删除主表记录时,把参照该主表记录的从表记录的外键设为null。如下SQL语句所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
create table teacher_table4
(
teacher_id int auto_increment,
teacher_name varchar(255),
primary key(teacher_id)
);
create table student_table4
(
student_id int auto_increment primary key,
student_name varchar(255),
java_teacher int,
foreign key(java_teacher)
references teacher_table4(teacher_id)
on delete cascade #也可以用on delete set null
);

13.2.5 数据库约束 3.PRIMARY KEY约束

主键约束相当于非空约束和唯一约束,即主键约束的列既不允许出现重复值,也不允许出现null值如果对多列组合建立主键约束,则多列里包含的每一列都不能为空,但只要求这些列组合不能重复。主键列的值可用于唯一地标识表中的一条记录。
每一个表中最多允许有一个主键,但这个主键约束可由多个数据列组合而成,
主键是表中能唯一确定一行记录的字段或字段组合

建立主键约束时既可使用列级约束语法,也可使用表级约束语法。如果需要对多个字段建立组合主键约束,则只能使用表级约束语法
使用表级约束语法来建立约束时,可以为该约束指定约束名。但不管用户是否为该主键约束指定约束名,MySQL总是将所有的主键约束命名为PRIMARY
提示:
MySQL允许在建立主键约束时为该约束命名,但这个名字没有任何作用,这是为了保持与标准SQL的兼容性。大部分数据库都允许自行指定主键约束的名字,而且一旦指定了主键约束名,则该约束名就是用户指定的名字。
当创建主键约束时,MySQL在主键约束所在列或列组合上建立对应的唯一索引。
创建主键约束的语法和创建唯一约束的语法非常像,一样允许使用列级约束语法为单独的数据列创建主键,如果需要为多列组合建立主键约束或者需要为主键约束命名,则应该使用表级约束语法来建立主键约束。与建立唯一约束不同的是,建立主键约束使用primary key

列级别约束创建主键约束

建表时创建主键约束,使用列级约束语法:

1
2
3
4
5
create table primary_test
(
test_id int primary key,
test_name varchar(255)
);

表级别约束创建主键约束

建表时创建主键约束,使用表级约束语法:

1
2
3
4
5
6
7
8
create table primary_test2
(
test_id int not null,
test_name varchar(255),
test_pass varchar(255),
# 指定主键约束名为test2_pk,对大部分数据库有效,但对MySQL无效
constraint test2_pk primary key(test_id)
);

组合主键

建表时创建主键约束,以多列建立组合主键,只能使用表级约束语法:

1
2
3
4
5
6
7
create table primary_test3
(
test_name varchar(255),
test_pass varchar(255),
# 建立多列组合的主键约束
primary key(test_name,test_pass)
);

删除主键约束

如果需要删除指定表的主键约束,则在alter table语句后使用drop primary key子句即可。SQL语句如下:

1
2
alter table primary_test3
drop primary key;

增加主键约束

如果需要为指定表增加主键约束,既可通过modify修改列定义来增加主键约束,这将采用列级约束语法来增加主键约束;也可通过add来增加主键约束,这将采用表级约束语法来增加主键约束。SQL语句如下:

通过add添加主键约束

1
2
alter table primary_test3
add primary key(test_name,test_pass);

通过modify添加主键约束

如果只是为单独的数据列增加主键约束,则可使用modify修改列定义来实现,如下SQL语句所示:

1
2
alter table primary_test3
modify test_name varchar(255) primary key;

注意
不要连续执行上面两条SQL语句,因为上面两条SQL语句都是为primary_test3增加主键约束,而同一个表里最多只能有一个主键约束,所以连续执行上面两条SQL语句肯定出现错误。为了避免这个问题,可以在成功执行了第一条增加主键约束的SQL语句之后,先将primary_test3里的主键约束删除后再执行第二条增加主键约束的SQL语句。

自动增长列

很多数据库对主键列都支持一种自增长的特性——如果某个数据列的类型是整型,而且该列作为主键列,则可指定该列具有自增长功能。指定自增长功能通常用于设置逻辑主键列——该列的值没有任何物理意义,仅仅用于标识每行记录。MySQL使用auto_increment来设置自增长,SQL语句如下:

1
2
3
4
5
6
create table primary_test4
(
test_id int auto_increment primary key,
test_name varchar(255);
test_pass varchar(255)
);

一旦指定了某列具有自增长特性,则向该表插入记录时可不为该列指定值,该列的值由数据库系统自动生成。

13.2.5 数据库约束 2.UNIQUE约束

唯一约束用于保证指定列或指定列组合不允许出现重复值。虽然唯一约束的列不可以出现重复值,但可以出现多个null值(因为在数据库中null不等于null
同一个表内可建多个唯一约束,唯一约束也可由多列组合而成。当为某列创建唯一约束时,MySQL会为该列相应地创建唯一索引。如果不给唯一约束起名,该唯一约束默认与列名相同。
唯一约束既可以使用列级约束语法建立,也可以使用表级约束语法建立。
如果需要为多列建组合约束,或者需要为唯一约束指定约束名,则只能用表级约束语法
当建立唯一约束时,MySQL在唯一约束所在列或列组合上建立对应的唯一索引。

使用列级约束语法建立唯一约束

使用列级约束语法建立唯一约束非常简单,只要简单地在列定义后增加unique关键字即可SQL语句如下:

1
2
3
4
5
create table unique_test
(
test_id int not null,
test_name varchar(255) unique
);

表级约束语法建立唯一约束

如果需要为多列组合建立唯一约束,或者想自行指定约束名,则需要使用表级约束语法。表级约束语法格式如下:

1
[constraint 约束名] 约束定义

上面的表级约束语法格式既可放在create table语句中与列定义并列,也可放在alter table语句中使用add关键字来添加约束。SQL语句如下:

1
2
3
4
5
6
7
8
9
10
create table unique_test2
(
test_id int not null,
test_name varchar(255),
test_pass varchar(255),
# 使用表级约束语句建立唯一约束
unique (test_name),
# 使用表级约束语法建立唯一约束,而且指定约束名
constraint test_2uk unique(test_pass)
)

上面的建表语句为test_nametest_pass分别建立了唯一约束,这意味着这两列都不能出现重复值。除此之外,还可以为这两列组合建立唯一约束,SQL语句如下:

1
2
3
4
5
6
7
create table unique_test3
(
test_id int not null,
test_name varchar(255),
test_pass varchar(255),
constranint test3_uk unique(test_name,test_pass)
);

对于上面的unique_test2unique_test3两个表,都是对test_nametest_pass建立唯一约束,其中unique_test2要求test_nametest_pass都不能出现重复值,而unique_test3只要求test_nametest_pass两列值的组合不能重复。

修改表结构时增加或删除唯一约束

也可以在修改表结构时使用add关键字来增加唯一约束,SQL语句如下:

1
2
alter table unique_test3
add unique(test_name,test_pass)

还可以在修改表时使用modify关键字,为单列采用列级约束语法来增加唯一约束,代码如下:

1
2
alter table unique_test3
modify test_name varchar(255) unique;

对于大部分数据库而言,删除约束都是在alter table语句后使用“drop constraint约束名”语法来完成的,但MySQL并不使用这种方式,而是使用“drop index 约束名”的方式来删除约束。例如如下SQL语句:

1
2
alter table unique_test3
drop index test3_uk;

13.2.5 数据库约束

前面创建的数据表仅仅指定了一些列定义,这仅仅是数据表的基本功能。除此之外,所有的关系数据库都支持对数据表使用约束,通过约束可以更好地保证数据表里数据的完整性约束是在表上强制执行的数据校验规则,约束主要用于保证数据库里数据的完整性。除此之外,当表中数据存在相互依赖性时,可以保护相关的数据不被删除。

5种完整性约束

大部分数据库支持下面5种完整性约束。

  • not null:非空约束,指定某列不能为空。
  • unique:唯一约束,指定某列或者几列组合不能重复。
  • primary key:主键,指定该列的值可以唯一地标识该条记录。
  • foreign key:外键,指定该行记录从属于主表中的一条记录,主要用于保证参照完整性
  • check:检查,指定一个布尔表达式,用于指定对应列的值必须满足该表达式

MySQL不支持check约束

虽然大部分数据库都支持上面5种约束,但**MySQL不支持CHECK约束**,虽然MySQLSQL语句也可以使用CHECK约束,但这个CHECK约束不会有任何作用。

约束分类

虽然约束的作用只是用于保证数据表里数据的完整性,但约束也是数据库对象,并被存储在系统表中,也拥有自己的名字。根据约束对数据列的限制,约束分为如下两类。

  • 单列约束:每个约束只约束一列。
  • 多列约束:每个约束可以约束多个数据列。

什么时候指定约束

为数据表指定约束有如下两个时机。

  • 建表的同时为相应的数据列指定约束。
  • 建表后创建,以修改表的方式来增加约束。

大部分约束都可以采用列级约束语法或者表级约束语法。下面依次介绍5种约束的建立和删除(约束通常无法修改)。
提示:
MySQL使用information_schema数据库里的TABLE_CONSTRAINTS表来保存该数据库实例中所有的约束信息,用户可以通过查询TABLE_CONSTRAINTS表来获取该数据库的约束信息。

1.NOT NULL约束

非空约束用于确保指定列不允许为空,非空约束是比较特殊的约束,它只能作为列级约束使用,只能使用列级约束语法定义。这里要介绍一下SQL中的null值,SQL中的null不区分大小写。SQL中的null具有如下特征。

  • 所有数据类型的值都可以是null,包括intfloatboolean等数据类型。
  • Java类似的是,空字符串不等于null,0也不等于null

建表的时候指定NOT NULL约束

如果需要在建表时为指定列指定非空约束,只要在列定义后增加not null即可。建表语句如下:

1
2
3
4
5
CREATE TABLE `hehe` (
`hehe_id` int(11) NOT NULL,
`hehe_name` varchar(255) NOT NULL,
`hehe_gender` varchar(2)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

not null约束的默认值

**在插入数据的时候,如果某列没有赋值,MySQL会赋给一个默认值.**
对于not null约束的列,

  • 如果类型为int,则默认值为0
  • 如果默认值为varchar,则默认值为空字符串.

实例

现在插入一条数据insert into hehe(hehe_gender) values('小王');,这里只给第三个属性赋值,第一和第二没有赋值,此时表中的数据如下所示:

1
2
3
4
5
6
7
8
9
10
mysql> insert into hehe(hehe_gender) values('小王');
Query OK, 1 row affected

mysql> select * from hehe;
+---------+-----------+-------------+
| hehe_id | hehe_name | hehe_gender |
+---------+-----------+-------------+
| 0 | | 小王 |
+---------+-----------+-------------+
1 row in set

可以看到第一列填入了默认值0,而第二列好像什么都没有填入,但其实第二列填入的是空字符串而不是NULL(not null约束的列当然不能填入null).
现在再来插入一条数据:insert into hehe(hehe_id) values(1);这里只填入第一列,第二列和第三列让MySQL默认填写.
查询结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mysql> select * from hehe;
+---------+-----------+-------------+
| hehe_id | hehe_name | hehe_gender |
+---------+-----------+-------------+
| 0 | | 小王 |
+---------+-----------+-------------+
1 row in set
mysql> insert into hehe(hehe_id) values(1);
Query OK, 1 row affected

mysql> select * from hehe;
+---------+-----------+-------------+
| hehe_id | hehe_name | hehe_gender |
+---------+-----------+-------------+
| 0 | | 小王 |
| 1 | | NULL |
+---------+-----------+-------------+
2 rows in set

因为第三列的hehe_gender默认值为NULL,所以MySQL给该列填上了默认值NULL,
而第二列的hehe_name使用了not null约束,不能填写NULL,此时MySQL填写上一个空字符串.

alter table修改表时增加和删除非空约束

除此之外,也可以在使用alter table修改表时增加或者删除非空约束,SQL命令如下:

增加非空约束

1
2
alter table hehe
modify hehe_gender varchar(2) not null

效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mysql> desc hehe;
+-------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| hehe_id | int(11) | NO | | NULL | |
| hehe_name | varchar(255) | NO | | NULL | |
| hehe_gender | varchar(2) | YES | | NULL | |
+-------------+--------------+------+-----+---------+-------+
3 rows in set

mysql> alter table hehe
modify hehe_gender varchar(2) not null;
Query OK, 3 rows affected
Records: 3 Duplicates: 0 Warnings: 2

mysql> desc hehe;
+-------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| hehe_id | int(11) | NO | | NULL | |
| hehe_name | varchar(255) | NO | | NULL | |
| hehe_gender | varchar(2) | NO | | NULL | |
+-------------+--------------+------+-----+---------+-------+
默认值

此时再次查询表中数据,可以发现第二行第三列的值已经变成列空字符串了,不再是原来的NULL,如下所示:

1
2
3
4
5
6
7
8
mysql> select * from hehe;
+---------+-----------+-------------+
| hehe_id | hehe_name | hehe_gender |
+---------+-----------+-------------+
| 0 | | 小王 |
| 1 | | |
+---------+-----------+-------------+
2 rows in set

取消非空约束并指定默认值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mysql> desc hehe;
+-------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| hehe_id | int(11) | NO | | NULL | |
| hehe_name | varchar(255) | NO | | NULL | |
| hehe_gender | varchar(2) | NO | | NULL | |
+-------------+--------------+------+-----+---------+-------+
3 rows in set

mysql> alter table hehe
modify hehe_name varchar(255) default 'xyz' null;
Query OK, 3 rows affected
Records: 3 Duplicates: 0 Warnings: 0

mysql> desc hehe;
+-------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| hehe_id | int(11) | NO | | NULL | |
| hehe_name | varchar(255) | YES | | xyz | |
| hehe_gender | varchar(2) | NO | | NULL | |
+-------------+--------------+------+-----+---------+-------+
3 rows in set
默认值

这时候我们再次查询:

1
2
3
4
5
6
7
8
mysql> select * from hehe;
+---------+-----------+-------------+
| hehe_id | hehe_name | hehe_gender |
+---------+-----------+-------------+
| 0 | | 小王 |
| 1 | | |
+---------+-----------+-------------+
2 rows in set

这里可以发现第二列原来的默认值(空字符串),并没有直接变成xyz.这是因为default语句定义的字符串默认值,在新插入的记录才有效(其实原来的空字符串也是字符串,MySQL该列已经有数据了不需要自己填写默认值,所以不会使用xyz覆盖原来的空字符串)
现在再插入一条记录insert into hehe (hehe_id) values(2);此时第二列没有赋值,将填入默认值xyz.如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mysql> select * from hehe;
+---------+-----------+-------------+
| hehe_id | hehe_name | hehe_gender |
+---------+-----------+-------------+
| 0 | | 小王 |
| 1 | | |
+---------+-----------+-------------+
2 rows in set

mysql> insert into hehe (hehe_id) values(2);
Query OK, 1 row affected

mysql> select * from hehe;
+---------+-----------+-------------+
| hehe_id | hehe_name | hehe_gender |
+---------+-----------+-------------+
| 0 | | 小王 |
| 1 | | |
| 2 | xyz | |
+---------+-----------+-------------+
3 rows in set
mysql>

13.2.4 DDL语句 3.删除表的语法

删除表的语法格式如下:

1
drop table 表名;

实例

如下SQL语句将会把数据库中已有的wawa数据表删除:

1
drop table wawa;

运行效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> show tables;
+-----------------------+
| Tables_in_test_engine |
+-----------------------+
| user_inf |
| wawa |
+-----------------------+
2 rows in set

mysql> drop table wawa;
Query OK, 0 rows affected

mysql> show tables;
+-----------------------+
| Tables_in_test_engine |
+-----------------------+
| user_inf |
+-----------------------+
1 row in set
mysql>

删除数据表的效果如下。

  • 表结构被删除,
  • 表对象不再存在。
  • 表里的所有数据也被删除。
  • 该表所有相关的索引、约束也被删除。

4.truncate表

对于大部分数据库而言,truncate都被当成DDL处理,truncate被称为“截断”某个表——它的作用是删除该表里的全部数据,但保留表结构。相对于DML里的delete命令而言,truncate的速度要快得多,而且truncate不像delete可以删除指定的记录,truncate只能一次性删除整个表的全部记录。truncate命令的语法如下:

1
truncate 表名;

实例

1
truncate user_inf;

运行效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
mysql> show tables;
+-----------------------+
| Tables_in_test_engine |
+-----------------------+
| user_inf |
+-----------------------+
1 row in set

mysql> select * from user_inf;
+----+------+
| id | name |
+----+------+
| 1 | 小明 |
| 2 | 小王 |
+----+------+
2 rows in set

mysql> truncate user_inf;
Database changed

mysql> show tables;
+-----------------------+
| Tables_in_test_engine |
+-----------------------+
| user_inf |
+-----------------------+
1 row in set

mysql> desc user_inf;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | varchar(10) | NO | PRI | NULL | |
| name | varchar(30) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set

mysql> select * from user_inf;
Empty set

mysql>

MySQLtruncate的处理比较特殊——如果使用非InnoDB存储机制,truncatedelete速度要快;如果使用InnoDB存储机制,在MySQL5.0.3之前,truncatedelete完全完全一样,在5.0.3之后,truncatetabledelete效率高,但如果该表被外键约束所参照,truncate又变为delete操作。在5.0.13之后,快速truncate总是可用,即比delete性能要好。

13.2.4 DDL语句 2.修改表结构的语法

修改表结构使用alter table,修改表结构包括增加列定义修改列定义删除列重命名列等操作。

增加列定义

增加列定义的语法如下:

1
2
3
4
5
6
alter table 表名
add
(
列名 类型 [default 默认值],
...
);

上面的语法格式中圆括号部分与建表语法的圆括号部分完全相同,只是此时圆括号里的列定义是追加到已有表的列定义后面。还有一点需要指出,如果只是新增一列,则可以省略圆括号,仅在add后紧跟一个列定义即可。

实例

现在为hehe表增加一个hehe_id字段,如下所示:

1
2
alter table hehe
add hehe_id int;

运行效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mysql> desc hehe;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | varchar(10) | NO | | NULL | |
| name | varchar(30) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set

mysql> alter table hehe
add hehe_id int;
Query OK, 2 rows affected
Records: 2 Duplicates: 0 Warnings: 0

mysql> desc hehe;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id | varchar(10) | NO | | NULL | |
| name | varchar(30) | YES | | NULL | |
| hehe_id | int(11) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
3 rows in set

再添加两个字段,如下所示:

1
2
3
4
5
6
alter table hehe
add
(
aaa varchar(255) default 'xxx',
bbb varchar(255)
);

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
mysql> desc hehe;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id | varchar(10) | NO | | NULL | |
| name | varchar(30) | YES | | NULL | |
| hehe_id | int(11) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
3 rows in set

mysql> alter table hehe
add
(
aaa varchar(255) default 'xxx',
bbb varchar(255)
);
Query OK, 2 rows affected
Records: 2 Duplicates: 0 Warnings: 0

mysql> desc hehe;
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | varchar(10) | NO | | NULL | |
| name | varchar(30) | YES | | NULL | |
| hehe_id | int(11) | YES | | NULL | |
| aaa | varchar(255) | YES | | xxx | |
| bbb | varchar(255) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+
5 rows in set

上面第二条SQL语句增加aaa字段时,为该字段指定默认值为'xxx'。值得指出的是,**SQL语句中的字符串值不是用双引号引起,而是用单引号引起的**。
增加字段时需要注意:如果数据表中已有数据记录,除非给新增的列指定了默认值,否则新增的数据列不可指定非空约束,因为那些已有的记录在新增列上肯定是空(实际上,修改表结构很容易失败,只要新增的约束与已有数据冲突,修改就会失败)。

修改列定义

修改列定于的语法如下:

1
2
3
alter table 表名
modify 列名 类型 [default 默认值]
[first|after col_name];

上面语法中first或者aftercol_name指定需要将目标修改到指定位置。
从上面修改语法中可以看出,该修改语句每次只能修改一个列定义,如下代码所示:

实例

hehe表的hehe_id该成varchar(255)类型

1
2
alter table hehe
modify hehe_id varchar(255);

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
mysql> desc hehe;
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | varchar(10) | NO | | NULL | |
| name | varchar(30) | YES | | NULL | |
| hehe_id | int(11) | YES | | NULL | |
| aaa | varchar(255) | YES | | xxx | |
| bbb | varchar(255) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+
5 rows in set

mysql> alter table hehe
modify hehe_id varchar(255);
Query OK, 2 rows affected
Records: 2 Duplicates: 0 Warnings: 0

mysql> desc hehe;
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | varchar(10) | NO | | NULL | |
| name | varchar(30) | YES | | NULL | |
| hehe_id | varchar(255) | YES | | NULL | |
| aaa | varchar(255) | YES | | xxx | |
| bbb | varchar(255) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+
5 rows in set
mysql>

将hehe表的bbb列修改成int类型

1
2
alter table hehe
modify bbb int;

运行效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
mysql> desc hehe;
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | varchar(10) | NO | | NULL | |
| name | varchar(30) | YES | | NULL | |
| hehe_id | varchar(255) | YES | | NULL | |
| aaa | varchar(255) | YES | | xxx | |
| bbb | varchar(255) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+
5 rows in set

mysql> alter table hehe
modify bbb int;
Query OK, 2 rows affected
Records: 2 Duplicates: 0 Warnings: 0

mysql> desc hehe;
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | varchar(10) | NO | | NULL | |
| name | varchar(30) | YES | | NULL | |
| hehe_id | varchar(255) | YES | | NULL | |
| aaa | varchar(255) | YES | | xxx | |
| bbb | int(11) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+
5 rows in set
mysql>

从上面代码中不难看出,使用SQL修改数据表里列定义的语法和为数据表只增加一个列定义的语法几乎完全一样,关键是增加列定义使用add关键字,而修改列定义使用modify关键字。还有一点需要指出,**add新增的列名必须是原表中不存在的,而modify修改的列名必须是原表中已存在的
注意
虽然
MySQL的一个modify命令不支持一次修改多个列定义,但其他数据库如Oracle支持一个modify命令修改多个列定义,一个modify命令修改多个列定义的语法和一个add命令增加多个列定义的语法非常相似,也需要使用圆括号把多个列定义括起来。如果需要让MySQL支持一次修改多个列定义,则可在alter table后使用多个modify命令**。

如果数据表里已有数据记录,则修改列定义非常容易失败,因为有可能修改的列定义规则与原有的数据记录不符合。如果修改数据列的默认值,则只会对以后的插入操作有作用,对以前已经存在的数据不会有任何影响

删除列的语法

从数据表中删除列的语法比较简单:

1
2
alter table 表名
drop column_name

删除列只要在drop后紧跟需要删除的列名即可

实例

删除hehe表中的aaa字段

1
2
alter table hehe
drop aaa;

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
mysql> desc hehe;
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | varchar(10) | NO | | NULL | |
| name | varchar(30) | YES | | NULL | |
| hehe_id | varchar(255) | YES | | NULL | |
| aaa | varchar(255) | YES | | xxx | |
| bbb | int(11) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+
5 rows in set

mysql> alter table hehe
drop aaa;
Query OK, 2 rows affected
Records: 2 Duplicates: 0 Warnings: 0

mysql> desc hehe;
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | varchar(10) | NO | | NULL | |
| name | varchar(30) | YES | | NULL | |
| hehe_id | varchar(255) | YES | | NULL | |
| bbb | int(11) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+
4 rows in set

从数据表中删除列定义通常总是可以成功,删除列定义时将从每行中删除该列的数据,并释放该列在数据块中占用的空间。所以删除大表中的字段时需要比较长的时间,因为还需要回收空间。

MySQL独有语法

上面介绍的这些增加列、修改列和删除列的语法是标准的SQL语法,对所有的数据库都通用。除此之外,MySQL还提供了两种特殊的语法:重命名数据表完全改变列定义

重命名数据表

重命名数据表的语法格式如下:

1
2
alter table 表名
rename to 新表名;

实例

如下SQL语句用于将hehe表命名为wawa

1
2
alter table hehe
rename to wawa;

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mysql> show tables;
+-----------------------+
| Tables_in_test_engine |
+-----------------------+
| hehe |
| user_inf |
+-----------------------+
2 rows in set

mysql> alter table hehe
rename to wawa;
Query OK, 0 rows affected

mysql> show tables;
+-----------------------+
| Tables_in_test_engine |
+-----------------------+
| user_inf |
| wawa |
+-----------------------+
2 rows in set
mysql>

改变列名

MySQLaltertable提供了change选项,该选项可以改变列名。change选项的语法如下:

1
2
3
alter table 表名
change 旧列名 新列名 [default 默认值]
[first|after 列明];

对比changemodify两个选项,不难发现:change选项比modify选项多了一个列名,因为**change选项可以改变列名,所以它需要两个列名。一般而言,如果不需要改变列名,使用alter tablemodify选项即可,只有当需要修改列名时才会使用change选项**。

实例

wawa数据表的bbb字段重命名为ddd:

1
2
alter table wawa
change bbb ddd int;

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
mysql> desc wawa;
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | varchar(10) | NO | | NULL | |
| name | varchar(30) | YES | | NULL | |
| hehe_id | varchar(255) | YES | | NULL | |
| bbb | int(11) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+
4 rows in set

mysql> alter table wawa
change bbb ddd int;
Query OK, 2 rows affected
Records: 2 Duplicates: 0 Warnings: 0

mysql> desc wawa;
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | varchar(10) | NO | | NULL | |
| name | varchar(30) | YES | | NULL | |
| hehe_id | varchar(255) | YES | | NULL | |
| ddd | int(11) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+
4 rows in set
mysql>

13.2.4 DDL语句

DDL语句是操作数据库对象的语句,包括创建(create)、删除(drop)和修改(alter)数据库对象。
前面已经介绍过,最基本的数据库对象是数据表,数据表是存储数据的逻辑单元。但数据库里绝不仅包括数据表,数据库里可包含如表13.1所示的几种常见的数据库对象。

表13.1常见的数据库对象
对象名称 对象关键字 描述
表格 table 表示存储数据的逻辑单元,以行和列的形式存在,列就是字段,行就是记录.
约束 constraint 执行数据校验的规则,用于保证数据完整性的规则
视图 view 一个或者多个数据表里数据的逻辑显示,视图并不存储数据
索引 index 用于提高查询性能,相当于书的目录
函数 function 用于完成一次特定的计算,具有一个返回值
存储过程 procedure 用于完成一次完整的业务处理,没有返回值,但可以通过传出参数将多个值传给调用环境。
触发器 trigger 相当于一个事件监听器,当数据库发生特定事件后,触发器被触发,完成相应的处理。
因为存在上面几种数据库对象,所以create后可以紧跟不同的关键字。例如,建表应使用create table,建索引应使用create index,建视图应使用create view……
dropalter后也需要添加类似的关键字来表示删除、修改哪种数据库对象。

创建表的语法

1
2
3
4
5
6
create table [模式名.] 表名
(
#可以有多个列定义
columnName1 datatype [default expr],
...
)

上面语法中圆括号里可以包含多个列定义,每个列定义之间以英文逗号(,)隔开,最后一个列定义不需要使用英文逗号,而是直接以括号结束
前面已经讲过,建立数据表只是建立表结构,就是指定该数据表有多少列,每列的数据类型,所以建表语句的重点就是圆括号里的列定义,列定义由列名、列类型和可选的默认值组成
例如下面的建表语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
create table test
(
# 整形通常使用int
test_id int,
# 小数点数
test_price decimal,
# 普通长度文本,使用default指定默认值
test_name varchar(255) default 'xxxx',
# 大文本类型
test_desc text,
# 图片
test_img blob,
test_date datetime
);

建表时需要指定每列的数据类型,不同数据库所支持的列类型不同,这需要查阅不同数据库的相关文档。MySQL支持如表13.2所示的几种列类型。

列类型 说明
tinyint 1字节整数
smallint 2字节整数
mediumint 3字节整数
int(integer) 4字节整数
bigint 8字节整数,这些整数类型的区别仅仅是表数范围不同.又可以分为有符号和无符号两种.
float 单精度浮点数
double 双精度浮点数
decimal(dec) 精确小数类型,相对于floatdouble不会产生精度丢失的问题.
date 日期类型,不能保存时间,把java.util.Date对象保存到date列时,时间部分将会丢失.
time 时间类型,不能保存日期,把java.utilDate对象保存到time列时,日期部分将会丢失.
datetime 日期,时间类型
timestamp 时间戳类型
year 年类型,仅仅保存时间的年份
char 定常字符串类型
varchar 可变长度字符串类型
binary 定长二进制字符串类型,它以二进制形式保存字符串
varbinary 可变长度二进制字符串类型,它以二进制形式保存字符串
tinyblob 1字节的二进制大对象,可用于存储图片,音乐等二进制数据,可存储255B
blob 2字节的二进制大对象,可存储64KB
mediumblob 3字节的二进制大对象,可存储16MB
longblob 4字节的二进制大对象,可存储4GB
tinytext 1字节的文本对象,可用于存储超长长度的字符串,可存储255B大小的文本
text 2字节的文本对象,可用于存储超长长度的字符串,也存储64KB大小的文本
mediumtext 3字节的文本对象,16MB
longtext 4字节的文本对象,4GB
enum('value1',value2',...) 枚举类型,该列的值只能是enum后括号里多个值的其中之一
set('value1','value2',...) 集合类型,该列的值可以是set后括号里多个值的其中几
上面是比较常见的建表语句,这种建表语句只是创建一个空表,该表里没有任何数据。如果使用子查询建表语句,则可以在建表的同时插入数据。子查询建表语句的语法如下:
create table [模式名]表名 [column[, column...]] as subquery;
上面语法中新表的字段列表必须与子查询中的字段列表数量匹配,创建新表时的字段列表可以省略,如果省略了该字段列表,则新表的列名与选择结果完全相同。下面语句使用子查询来建表。
1
2
3
create table hehe
as
select * from user_inf;

因为上面语句是利用子查询来建立数据表,所以执行该SQL语句要求数据库中已存在user_inf数据表,通过如下SQL语句来创建和初始化user_inf:

1
2
3
4
5
6
7
8
DROP TABLE IF EXISTS `user_inf`;
CREATE TABLE `user_inf` (
`id` varchar(10) NOT NULL,
`name` varchar(30) CHARACTER SET utf8mb4 DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `user_inf` VALUES ('1', '小明');
INSERT INTO `user_inf` VALUES ('2', '小王');

下面查看两个表中的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mysql> select * from user_inf;
+----+------+
| id | name |
+----+------+
| 1 | 小明 |
| 2 | 小王 |
+----+------+
2 rows in set
mysql> select *from hehe;
+----+------+
| id | name |
+----+------+
| 1 | 小明 |
| 2 | 小王 |
+----+------+
2 rows in set

可以发现两个表中的数据一模一样,使用子查询可以在创建表的同时插入了其他表中的数据.

13.2.3 SQL语句基础

SQL的全称是Structured Query Language,也就是结构化查询语言SQL是操作和检索关系数据库的标准语言,标准的SQL语句可用于操作任何关系数据库。
使用SQL语句,程序员和数据库管理员(DBA)可以完成如下任务。

  • 在数据库中检索信息
  • 对数据库的信息进行更新
  • 改变数据库的结构
  • 更改系统的安全设置。
  • 增加或回收用户对数据库、表的许可权限。

在上面5个任务中,一般程序员可以管理前3个任务,后面2个任务通常由DBA来完成。

标准的SQL语句类型

标准的SQL语句通常可分为如下几种类型。

  • 查询语句:主要由select关键字完成,查询语句是SQL语句中最复杂、功能最丰富的语句。
  • DML (Data Manipulation Language,数据操作语言)语句:主要由insertupdatedelete三个关键字完成。
  • DDL (Data Definition Language,数据定义语言)语句:主要由createalterdroptruncate四个关键字完成。
  • DCL (Data Control Language, 数据控制语言)语句:主要由grantrevoke两个关键字完成。
  • 事务控制语句:主要由commitrollbacksavepoint三个关键字完成。

SQL语句的关键字不区分大小写,也就是说,create和CREATE的作用完全一样。
在上面5种SQL语句中,DCL语句(数据控制语言)用于为数据库用户授权,或者回收指定用户的权限,通常无须程序员操作,所以本节不打算介绍任何关于DCL的知识。

标识符

在SQL命令中也可能需要使用标识符,标识符可用于定义表名、列名,也可用于定义变量等。这些标识符的命名规则如下。

  • 标识符通常必须以字母开头。
  • 标识符包括字母、数字和三个特殊字符(,_,$)。
  • 不要使用当前数据库系统的关键字、保留字,

通常建议使用多个单词连缀而成,单词之间以_分隔。同一个模式下的对象不应该同名,这里的模式指的是外模式。

注意
truncate是一个特殊的DDL语句,truncate在很多数据库中都被归类为DDL,它相当于先删除指定的数据表,然后再重建该数据表。如果使用MySQL的普通存储机制,truncate确实是这样的。但如果使用InnoDB存储机制,则比较复杂,MySQL5.0.3之前,truncatedelete完全一样;在5.0.3之后,truncatedelete效率高,但如果该表被外键约束所参照,则依然被映射成delete操作。当使用快速truncate时,该操作会重设自动增长计数器。在5.0.13之后,快速truncate总是可用,即比delete性能要好。关于truncate的用法,请参考本章后面内容。