# 修改表名
# 相关知识
修改表,包括更改表的名称,删除表中的列、约束,为表添加新的列、约束,修改表中列的名称、数据类型和约束等操作,均通过 Alter Table 语句来实现。
# ALTER TABLE 语句
Alter Table 语句用于修改由 Create Table 语句创建的表的结构。比如,添加或删除列,添加或删除约束,创建或销毁索引,更改列的数据类型,更改列名甚至表名等。
ALTER TABLE 的完整语法较复杂,这里仅介绍简化后的语法:
ALTER TABLE 表名 | |
[修改事项 [, 修改事项] ...] |
可见,在一条 ALTER TABLE 语句里,可以同时对表作多项修改。可选的修改事项有很多,常用的有:
修改事项 ::= | |
ADD [COLUMN] 列名 数据类型 [列约束] | |
[FIRST | AFTER col_name] | |
| ADD {INDEX|KEY} [索引名] [类型] (列1,...) | |
| ADD [CONSTRAINT [约束名]] 主码约束 | |
| ADD [CONSTRAINT [约束名]] UNIQUE约束 | |
| ADD [CONSTRAINT [约束名]] 外码约束 | |
| ADD [CONSTRAINT [约束名]] CHECK约束 | |
| DROP {CHECK|CONSTRAINT} 约束名 | |
| ALTER [COLUMN] 列名 {SET DEFAULT {常量 | (表达式)} | DROP DEFAULT} | |
| CHANGE [COLUMN] 列名 新列名 数据类型 [列约束] | |
[FIRST | AFTER col_name] | |
| DROP [COLUMN] 列名 | |
| DROP {INDEX|KEY} 索引名 | |
| DROP PRIMARY KEY | |
| DROP FOREIGN KEY fk_symbol | |
| MODIFY [COLUMN] 列名 数据类型 [列约束] | |
[FIRST | AFTER col_name] | |
| RENAME COLUMN 列名 TO 新列名 | |
| RENAME {INDEX|KEY} 索引名 TO 新索引名 | |
| RENAME [TO|AS] 新表名 |
归纳一下,修改事项主要有:
- 用 ADD 关键词添加列和约束 (主码、外码、CHECK、UNIQUE 等约束);
- 用 DROP 关键词删除列、约束和索引 (含 Unique);
- 用 MODIFY 关键词修改列的定义 (数据类型和约束);
- 用 RENAME 关键词修改列、索引和表的名称;
- 用 CHANGE 关键词修改列的名称,同时还可以修改其定义 (类型和约束)。
需要说明的是:
- 注意 RENAME,MODIFY 和 CHANGE 的区别:仅改列名,用 RENAME; 只改数据类型不改名,用 MODIFY; 既改名又改数据类型,用 CHANGE。
- 在用 MODIFY,CHANGE 更改列的数据类型和约束时,修改后的 CHECK 约束并不会生效 (MySQL 只作语法检查,并未实现代码 -- 至少 MySQL 8.0.22 还未实现)。但用 ADD 新增列的 CHECK 约束,是有效的。另外,用 ADD 新增的 CHECK 约束,也会生效。
- 删除主码约束只能用 Drop Primary Key 短语,不能使用 drop constraint 短语,即便在创建主码约束时显式命名了该主码约束。试图使用 “drop constraint 主码约束名” 短语删除主码,会给出错误提示,显示该约束并不存在。因为 MySQL 并没有完全实现 “constraint 约束名 primary key (...)” 短语的功能,仅作了语法检查,然后直接忽略了主码约束的命名。
- 给已有列增加 Default 约束,可用 “alter 列 set default ...” 短语;删除列的 default 约束,可用 “alter 列 drop default” 短语。当然,也可以用 “Modify 列名 数据类型 ...” 短语。如果该短语没有 default 约束,就相当于删除了原来的 default 约束,如果该短语带有 default 约束,就相当于添加了 default 约束,如果之前已有 default 约束,则新的 Default 约束将代替原有的 Default 约束;
- 删除 unique 约束,既可用 “drop constraint 约束名” 短语,也可以用 “drop key 索引名” 短语来实现,唯一性 (unique) 约束实际是用 Unique 索引来实现的,Unique 索引的名字总是与 Unique 约束名完全一样,它们本就是同一套机制。如果没有显式命名的话,Unqiue 索引名或者说 Unique 约束名一般与列同名 (组合属性作索引,则与组合属性中的第 1 列同名)。但要注意是的,在更改列名后,Unique 索引名并不会随之更改。在创建 Unqiue 约束时,用 “constriant” 短语给约束取一个有意义的名字,是一个值得推荐的习惯。
# 如何更改表名
alter table 表名 rename [TO|AS] 新表名 |
# 添加与删除字段
# 如何给表添加字段
随着业务发展的需要,可能需要在原有表结构的基础上添加新的字段,由于建表时的疏忽,遗漏了某个字段,也需要将遗漏的列添加到表结构中。给表添加字段的语法是:
ALTER TABLE 表名 ADD [COLUMN] 列名 数据类型 [列约束] [FIRST | AFTER 列名] |
在 alter table 语句中指定表名,接着用关键字 add column 申明要添加的列,包括列的名称,数据类型,以及可选的列约束。列约束可以是主码约束、外码约束、CHECK 约束、Default 约束、Unique 约束等其中的任何一个或一组约束。这些约束在之前的实验中都介绍过。最后,还可以指定新添加的列在表中的位置:
- 关键字 FIRST 指示新添加的列为第 1 列;
- AFTER 指示新添加的列紧跟在指定列的后面。
如果省略位置指示,则新添加的列将成为表的最后一列。
关键字 column 可以省略。
示例:
学生表 student 的结构如下:
用以下语句创建了表 student:
create table student( | |
sno char(10) primary key, | |
sname varchar(32) not null, | |
sex char(2), | |
age int | |
); |
你可能已经注意到了,这条建表语句遗漏了列 sex 的约束,这个问题我们将在后面的实验中再解决,这里先考虑其它事情:
由于业务的发展,产生了新的需求,需要为该表添加一个字段,用于存储学生的手机号码:
怎么将列 mobile(以及对该列的完整性约束)添加到表 student 中呢?执行下面的语句即可:
alter table student | |
add mobile char(11) constraint CK_student_mobile check(mobile rlike '1[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]') |
这个 check 约束中的 rlike 还可以用 regexp 替代,它们是同义语。跟 Oracle 一样,MySQL 用正则匹配表达式来测试字段值是否符合某个 pattern,rlike 比 like 关键词所支持的功能要强大得多。
成功添加 mobile 列后,可以试着执行以下几条语句观察效果:
insert into student values('1','ZHAO','男',18,'13907106666'); |
这条语句将插入一条数据到表 student 中;
再尝试执行:
insert into student values('2','LI','女',18,'23907106666'); |
这一次,系统将输出报错信息:ERROR 3819 (HY000): Check constraint 'CK_student_mobile' is violated.
这条错误信息提醒我们:插入的数据违反了 CK_student_mobile 这条约束规则 (手机号码第 1 位必须是 1)。
你也可以试着把手机号码的第 1 位改为 1,中间的某个数字换成字母,然后再试着插入,同样会触发错误。
# 如何删除表中的字段
删除字段,即从表中将某个列移出,其语法格式为:
ALTER TABLE 表名 DROP [COLUMN] 列名 |
关键字 COLUMN 可以省略。
示例:
回到上面的例子(表 student),假设在实际运行过程中,有人对数据库的设计提出了质疑:在学生档案里记录年龄的作法并不科学,因为年龄会随着时间的变化而变化,档案里记录 17 岁,还得根据当年记录的日期以及当下的日期推算实际年龄。替代方案是记录出生日期而不是年龄:
有两个方案实现这个改动:
- 用
alter table student change age DOB date
直接改变列名和类数据类型; - 先用
alter table sudent add DOB date
添加列 DOB,再用alter table student drop age
删除列 age。
如果表 student 是空表,则上述两个方案都是可行的。但在实际运行过程中,表 student 可能已经存储了数据,直接将一个 int 型的列改成 date 型的列,将出现类型不匹配错误。
另一方面,如果简单粗暴地删除列 age,同时添加 DOB 列,则原来存储的年龄信息都将丢失。
最好的方案是先添加列 DOB,然后根据当年记录时的年龄推算其大致的出生日期(相当于等价地保留了年龄数据)。在作完这个数据转换工作后,再剔除列 age。这个工作共分三步:
第 1 步,添加列 DOB:
alter table student add DOB date after sex; |
列 DOB 将位于 sex 之后,age 之前。
第 2 步,根据 age 推算 DOB:
由于表 student 没有记录登记年龄的日期(实际系统总会记录学生是哪个年级的,学号中也往往包含入学年份信息),这里我们假设学生是 2020 年 9 月 1 日入学的:
update student set DOB = date_add('2020-9-1', interval -1*age year); |
(注:date_add () 是 MySQL 的函数。)
执行完这条 update 语句后,可以执行下面这条语句查看表 student 的内容:
select * from student; |
你将看到:
DOB 列成功地转录了 age 的信息(以另一种表现形式)。
第 3 步,删除列 age:
alter table student drop age; |
# 修改字段
# 相关知识
1.ALTER TABLE 语句的用法;
2. 如何修改字段的名称;
3. 如何修改字段的数据类型和约束;
4. 如何修改字段在表中的位置。
# Alter Table 语句
ALTER TABLE 表名 | |
[修改事项 [, 修改事项] ...] |
与修改列名、列数据类型和列约束,以及列序的修改事项有:
修改事项 ::= | |
ALTER [COLUMN] 列名 {SET DEFAULT {常量 | (表达式)} | DROP DEFAULT} | |
| CHANGE [COLUMN] 列名 新列名 数据类型 [列约束] | |
[FIRST | AFTER col_name] | |
| MODIFY [COLUMN] 列名 数据类型 [列约束] | |
[FIRST | AFTER col_name] | |
| RENAME COLUMN 列名 TO 新列名 |
其中 CHANGE 短语可修改列名、数据类型和列约束;MODIFY 短语可修改列的数据类型和约束;RENAME 短语仅用于更改列名;ALTER 短语仅用于修改列的 DEFAULT 约束或删除列的 DEFAULT 约束。CHANGE 和 MODIFY 短语还可以修改列在表中的位置。
# 如何修改字段的名称
如果只需要修改列的名称,显然用 RENAME 短语最简单:
ALTER TABLE 表名 RENAME COLUMN 列名 TO 新列名 |
注意:关键字 COLUMN 不能省略。
如果修改列名的同时,还要修改列的类型和约束 (相当于删除之前的列,再插入一个全新的列,且插入的位置还可以随意指定),则用 CHANGE 短语:
ALTER TABLE 表名 CHANGE [COLUMN] 列名 新列名 数据类型 [列约束] [FIRST | AFTER col_name] |
关键字 column 可以省略,列约束和列位置都是可选的。如果新列带有 CHECK 约束的话,MySQL 只会对这个约束作语法检查,并不会去实现这个约束,其它类型的约束没有问题。如果真有这样的需求,不如先 DROP 之前的列,再 ADD 新的列,新列附带的 CHECK 约束是会被实现的。
# 如何修改字段的数据类型和约束
如果列名称不变,仅需要修改其数据类型和约束,则用 MODIFY 短语:
ALTER TABLE 表名 MODIFY [COLUMN] 列名 数据类型 [列约束] [FIRST | AFTER col_name] |
在修改数据类型和约束的同时,还可以改变列在表中的位置。
注意,一旦使用 MODIFY 短语修改列,则该列之前的数据类型、约束将被新的数据类型和约束取而代之。如果之前定义了列约束,修改后不带列约束,相当于删除了之前的约束。
如果需要修改 (或添加) 列的 DEFAULT 约束,则既可用上面的 MODIFY 短语,也可以使用 ALTER 短语:
ALTER TABLE 表名 ALTER [COLUMN] 列名 SET DEFAULT {常量 | (表达式)} |
删除列的 DEFAULT 约束,则可以使用 ALTER 短语 (或 MODIFY 短语):
ALTER TABLE 表名 ALTER [COLUMN] 列名 DROP DEFAULT |
# 如何修改字段在表中的位置
如果仅需修改列在表中的位置,仍用 MODIFY 短语:
ALTER TABLE 表名 MODIFY [COLUMN] 列名 数据类型 [列约束] [FIRST | AFTER col_name] |
需要把列名、数据类型和约束完整地重述一遍,并在其后带上位置指示短语:FIRST 或 AFTER 某个列。注意:如果数据类型和约束重述的跟之前的不一样,则相当于修改了这个列,如果重述的列名跟之前的不一样,则会抛出错误信息(列不存在)。
示例
居民登记表 resident 的结构如下
表中,居民身份证号 idNo 为我国于 1984 年开始使用的第 1 代身份证号,只有 15 位编码 (其中 7、8 两位为出生年份)。1999 年,居民身份证编号由 15 位升至 18 位:年份用 4 位表达,并在末尾增加了 1 位校验码。现在需要对表 resident 作如下修改:
- 将身份证号 idNo 升级到 18 位;
- 身高 height 的类型改为无符号整数;
- 列名 educationalBackground 改为更简洁的 education。
可以用三条 alter table 语句分别实现上述需求,当然,更便捷的方法是用一条语句:
alter table resident modify idNo char(18), | |
modify height int unsigned, | |
rename column educationalBackground to education; |
# 添加、删除与修改约束
# 相关知识
- 如何删除和添加主码约束;
- 如何删除和添加外码约束;
- 如何删除和添加 CHECK 约束;
- 如何删除和添加 UNIQUE 约束。
# Alter table 语句与约束
Alter Table 语句与修改约束有关的部分:
ALTER TABLE 表名 | |
[修改事项 [, 修改事项] ...] |
删除与添加约束的修改事项有:
修改事项 ::= | |
| ADD [CONSTRAINT [约束名]] 主码约束 | |
| ADD [CONSTRAINT [约束名]] UNIQUE约束 | |
| ADD [CONSTRAINT [约束名]] 外码约束 | |
| ADD [CONSTRAINT [约束名]] CHECK约束 | |
| DROP {CHECK|CONSTRAINT} 约束名 | |
| DROP {INDEX|KEY} 索引名 | |
| DROP PRIMARY KEY | |
| DROP FOREIGN KEY fk_symbol |
可见,删除约束主要通过 alter table 语句的 drop 短语,添加约束则通过 alter table 语句的 add 短语来实现。
# 主码的删除与添加
删除主码:
ALTER TABLE 表名 DROP PRIMARY KEY; |
或者:
drop index `PRIMARY` on 表名; |
添加主码:
ALTER TABLE 表名 ADD [CONSTRAINT [约束名]] PRIMARY KEY(列1,列2,...); |
创建主码时,MySQL 将创建主码索引;删除主码,即意味着删除主码索引。反过来,删除主码索引,也意味着删除了主码约束。
迄今为止,MySQL 尽管在语法上支持主码约束的命名,但实际上并没有真正实现主码约束的命名功能。即,MySQL 并不会创建用户语句中所指定的约束名。所以,试图通过约束名删除主码约束是行不通的。
MySQL 中,所有的主码约束 (主码索引) 名均为 PRIMARY,无论怎么命名或更命,这个名字都不会改变。由于 PRIMARY 是 MySQL 的保留字,所以,在引用这个主码约束 (索引) 名时,必须用一对 `` 符号将 PRIMARY 括起来。
# 主码约束举例
例如,表 score 用于记录学生选修课程的成绩,其结构如下:
组合属性 (sno,cno) 是该表的主码。由于疏忽,用以下语句创建了该表:
create table score( | |
sno char(10), | |
cno char(10) not null, | |
grade int, | |
constraint PK_score primary key(sno) | |
); |
显然,主码定义错了,应当删除重建:
第 1 步:删除错误的主码定义:
alter table score drop primary key; |
第 2 步:重新创建主码:
alter table score add constraint PK_score primary key(sno,cno); |
或者简单地写成:
alter table score add primary key(sno,cno); |
以上两种写法的结果是一样的。因为 MySQL 会无视主码约束的命名短语 (仅作语法检查),不会真正给主码约束命名。
当然,上述删除并重建主码的工作可用一条语句完成:
alter table score drop primary key, add primary key(sno,cno); |
注意多个修改项之间用逗号分隔。
# 外码的删除与添加
alter table 语句提供了两个短语用来删除外码约束:
ALTER TABLE 表名 DROP CONSTRAINT 约束名 |
或:
ALTER TABLE 表名 DROP FOREIGN KEY 约束名 |
删除外码约束,必须显式给出外码约束的名字。
添加外码约束:
ALTER TABLE 表名 ADD [CONSTRAINT [约束名]] 外码约束 |
约束名是可选的,如果省略命名短语,MySQL 将按一定的规则自动命名。将来如果要删除该约束,必须先查询到该约束的名字(注:从 MySQL 的数据字典查询)。
创建外码时,MySQL 将同步创建外码索引,如果外码约束有显式命名,则外码索引与外码约束同名。如果外码约束未命名,则外码索引与外码列的列名同名。
删除外码约束时,外码索引不会跟着删除。如果将来重新创建了外码,并显式命名,则外码索引会自动更名 (与外码约束名保持相同)。
# Check 约束的删除与添加
删除 check 约束:
ALTER TABLE 表名 DROP CONSTRAINT 约束名 |
添加 check 约束:
ALTER TABLE 表名 ADD [CONSTRAINT [约束名]] check(条件表达式) |
同样地,如果未显式命名 check 约束,MySQL 将按一定规则自动予以命名。
添加约束时,如果现有数据与该约束规则相矛盾,则创建约束的请求会被拒绝。
# Unique 约束的删除与添加
删除 Unique 约束:
alter table 表名 drop constraint 约束名; |
或者:
drop index 索引名 on 表名; |
添加 Unique 约束:
alter table 表名 ADD [CONSTRAINT [约束名]] UNIQUE(列1,...) |
创建 unique 约束时,将同步创建 unique 索引,索引名与约束同名。如果未显式命名 unique 约束或索引,MySQL 将按一定规则自动命名(单列的 unique 索引或约束与列同名)。
是故,删除 unique 索引,等同于删除 unique 约束。反过来,删除 unique 约束,也等同于删除了 unique 索引。
# 举例
现在院系表 dept 和学生表 student,两表结构分别如下:
院系表:dept
学生表:student
除了两个主码和一个外码以下,表 student 还存在其它约束规则:
- 手机号码是唯一的,不允许重复;
- 手机号码第 1 位必须是 1;
- 姓名是唯一的,不允许同名。
建表语句如下:
create table dept( | |
deptNo char(3) primary key, | |
deptName char(50), | |
addr varchar(100) | |
) | |
create table student( | |
sno char(10) primary key, | |
sname char(30) unique, | |
mobile char(11), | |
dept char(3), | |
constraint UN_student_mobile unique(mobile), | |
constraint FK_student_1 foreign key (dept) references dept(deptNo), | |
constraint CK_student_mobile check(mobile like '1%') | |
); |
显然,sname 列上的 unique 约束未命名,而 mobile 列上的 unique 约束则给予了显式命名。
现在对表 student 的约束作如下调整:
- 列 sname 上的 unique 约束不合理,删除该约束;
- 列 dept 上的外码约束名改为:FK_student_dept;
- 列 mobile 上的 check 约束改为:电话号码第 1 位为 1,接下来 10 位为 0-9 之间的数字。
约束的修改一般通过先删除旧约束再重建新约束来实现。
(1) 列 sname 上的 unique 未命名,其约束名与列名相同。可以通过以下语句删除该约束:
alter table student drop constraint sname;\ |
或
drop index sname on student; |
(2) 为列 dept 上的外码约束改名:
alter table student drop constraint FK_student_1; | |
alter talbe student add constraint FK_student_dept foreign key (dept) references dept(deptNo); |
(3) 重新定义对 mobile 列的 check 约束:
alter table student drop constraint CK_student_mobile; | |
alter table student add constraint CK_student_mobile check(mobile regexp '1[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]') |