数据操纵语言DML
数据操纵语言(Data Manipulation Language,DML)是 SQL 语言的核心部分之一。在添加、更新或者删除表中的数据时,需要执行 DML 语句。很多时候我们提到数据库的基本操作,都会说增、删、改、查,为什么 DML 里面没有查询?因为 SELECT
查询语句属于数据查询语言 DQL,不属于数据操纵语言 DML,只是在日常工作中,多数研发人员、数据库管理员都习惯性的将 SELECT
语句归入数据操纵语言中,这一点需要弄清楚
insert
NSERT 语句用于向表中插入新的记录。其基本语法形式如下:
INSERT INTO table[(column1, column2…)] VALUES (value1, value2…)
其中 table
是表名;column1, column2…
是表中的字段名列表,用 ,
隔开;value1, value2…
是字段对应的值列表。
INSERT 语句的两种形式
第一种
第一种形式无需指定要插入数据的列名,只需提供被插入的值即可。
第二种
第二种形式就是表名后面跟字段名,此时字段名的顺序可以随意指定,不需要按照表中字段的顺序列出。
自增字段的处理
在表中我们可以设置整数类型的字段进行自动增长,city 表中的 ID 字段拥有自动增长属性。设置了该属性的字段,在进行插入操作的时候,其值使用 NULL 来代替。
这里需要强调两点:
- 在插入记录的时候,我们一般要求表名后面跟上字段名,这样对于软件开发人员来说,提高了代码的可读性。
- 在插入数据的时候需要满足该表的其它约束,city 表的 CountryCode 字段就存在一个外键约束
UPDATE 语句
修改数据也是经常用到的数据库管理操作。在数据库中使用 UPDATE
语句对数据进行修改,其基本语法形式如下:
UPDATE table SET column1 = value1, column2 = value2 [WHERE子句]
UPDATE 和 SET 是该语法的固定关键字。其中 table 表示的是表名,column1、column2 表示的是要修改的字段名,value1、value2 表示的是修改后的值。
值得注意的是,在使用 UPDATE
语句时,通常需要使用 WHERE
子句进行条件限制,用来指定被修改的行。如果没有 WHERE
子句,则表中所有的记录都会被修改。
和 INSERT
语句一样,使用 UPDATE
语句时也要注意字段的约束控制。
DELETE 语句
数据库的操作语言 DML 包括 INSERT
语句、UPDATE
语句以及 DELETE
语句。
DELETE FROM table [WHERE…]
其中 table 表示表名,如果 DELETE
语句中不写 WHERE
子句,那么删除的将会是表中的所有数据。
同时删除多条记录可以使用 IN 链接符来完成。
利用 DELETE
语句删除记录的时候,一定要结合着 WHERE
子句来进行精确删除。DELETE
语句后不跟 WHERE
子句的一个用途,就是进行清空数据操作。
事务处理语言 TCL
在进行数据操作的时候,往往会有很多不确定事件发生,这些事情通常都具有破坏性,导致数据不一致或者缺失等等。例如在银行 ATM 机上取 5000 现金,点钱过程中突然断电了,只取出一部分,而卡中余额提示已经扣掉了 5000,怎么办?这种时候,我们就需要一种机制来保证数据结果的一致性。在数据库中,这个机制就叫做事务。
事务(Transaction)是数据库操作的最小工作单元,是作为单个逻辑执行的一系列操作(比如上述中的输入取款金额,得到现金,取回磁卡等操作)的集合;这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行。事务是不可再分割的。
事务具有四大特征,即常说的 ACID:
- 原子性(Atomicity):事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做。
- 一致性(Consistency):事务执行的前后数据都处于合法状态,不会违背任何的数据完整性和逻辑的正确性,这就是“一致”的意思。以转账为例,转出账户的钱减少,但转入账户的钱没有增加,就不符合一致性。再以转账为例,无论多少个账户,多少个并行事务,其总数必然是一致的。原子性有助于保证数据的一致性,但不能完全保证。
- 隔离性(Isolation):一个事务的执行不能被其它事务所干扰,即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。换句话说,事务之间感知不到彼此的存在。
- 持续性(Durability):又叫永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
事务控制
事务的控制是通过一系列控制语句来完成的,代表着事务的各个阶段。常用的控制语句如下表所示:
语句 | 说明 |
---|---|
BEGIN(START TRANSACTION) | 开始一个新的事务 |
SAVEPOINT | 设置事务的保存点 |
COMMIT | 提交事务 |
ROLLBACK | 回滚当前事务到初始状态,撤销提交前的操作 |
ROLLBACK TO SAVEPOINT name | 回滚当前事务到指定保存点 name,并撤销保存点后的操作 |
SET AUTOCOMMIT | 设置当前连接是否自动提交事务,1 表示启用自动提交,0 表示禁用自动提交 |
RELEASE SAVEPOINT | 释放保存点 |
SET TRANSACTION | 设置事务的隔离级别 |
查看当前事务是否设置了自动提交,其 SQL 语句如下:
SELECT @@AUTOCOMMIT;
输出结果:
该值为 1,即 MySQL 默认启用事务的自动提交模式,不用再输入提交命令。
为了演示事务的特点,我们把事务提交方式设置为禁止自动提交。其 SQL 语句如下:
SET AUTOCOMMIT = 0;
查看修改结果:
SELECT @@AUTOCOMMIT;
操作截图:
接着利用 DELETE
子句,把 city 表中 ID 为 1 的记录给删除掉,其 SQL 语句如下:
DELETE FROM city WHERE ID = 1;
接着查询 id 为 1 的记录此时是否存在,使用 SQL 语句如下:
SELECT * FROM city WHERE ID = 1;
输出结果:
从结果上看,数据已经被删除。但此时事务并没有提交,所以当前对数据的操作还没有完全写入到数据库中,可以使用 ROLLBACK
来回滚事务来返回到该操作之前,其 SQL 语句如下:
ROLLBACK;
输出结果:
再次查看 id 为 1 的记录是否存在:
SELECT * FROM city WHERE ID = 1;
输出结果:
通过回滚操作,数据又回来了。只要在正式提交之前,都可以进行数据回滚。提交事务使用的控制语句是 COMMIT
,其 SQL 语句如下:
COMMIT;
确保数据的修改无误后,使用 COMMIT 来提交数据,我们也把这种方式称为显示提交。
我们现在开启一个新事务,并且修改 ID 为 6 的 Name
为 Rotter
:
BEGIN;
UPDATE city SET Name = Rotter WHERE ID = 6;
SELECT * FROM city WHERE ID = 6;
接着我们设置一个保存点 s1
,并把 ID 为 6 的 Population
设置为 600000:
SAVEPOINT s1;
UPDATE city SET Population = 600000 WHERE ID = 6;
SELECT * FROM city WHERE ID = 6;
这时我们看到 Population
已经修改完成,如果我们想要回滚到修改 Population
之前的状态,可以使用 ROLLBACK
语句
ROLLBACK TO s1;
这时我们再查看一下数据库中的数据,可以看到 Population 已经恢复到原先的数据:
SELECT * FROM city WHERE ID = 6;
这时我们可以选择释放 s1
这个保存点:
RELEASE SAVEPOINT s1;
结束当前事务可以选择 COMMIT
, 注意操作后保存点全部都会被释放。
事务保存点的设置和回滚
- 开始事务;
- 然后在 city 表中插入一条记录;
- 接着设置一个保存点;
- 再插入一条记录;
- 查看表内容变化情况;
- 然后回滚到保存点;
- 再次查看表内容;
- 最后回滚到事务开始的地方;
- 查看表内容,此时表内容和事务开始前是一样的,没有任何新的记录增加。
上面事务步骤翻译成 SQL 语句如下:
-
BEGIN; # 开始事务
输出结果:
-
# 在 city 表中插入一条记录 INSERT INTO city VALUES (NULL, 'test1', 'AFG', 'test1', 101);
输出结果:
-
SAVEPOINT s1; # 设置一个保存点
输出结果:
此处设置了保存点,取名为 s1。
-
INSERT INTO city VALUES (NULL, 'test2', 'AFG', 'test2', 10001); # 插入一条记录
输出结果:
SELECT * FROM city WHERE CountryCode = 'AFG'; # 查看表内容变化情况
输出结果:
此时可以看到,新插入的两条记录都显示出来了。
ROLLBACK TO SAVEPOINT s1; # 回滚到保存点
输出结果:
回滚到保存点 s1 位置处。
# 再次查看表内容
SELECT * FROM city WHERE CountryCode = 'AFG';
输出结果:
可以看到设置保存点 s1 之后的操作(插入的第二条记录)已经实现回滚,数据没有了。
ROLLBACK; # 回滚到事务开始的地方
输出结果:
此时回滚到了事务开始的地方。
SELECT * FROM city WHERE CountryCode = 'AFG'; # 查看表内容
输出结果:
再次查看结果,可以发现 city 表的内容和事务开始之前一模一样,没有任何变化。
不隔离并发事务的三个问题
事务并发是指多个事务同时对同一个数据进行操作。并发事务未做到隔离性,会带来以下问题:
脏读
一个事务读取到另一事务未提交的更新数据。当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中。这时,另外一个事务也访问这个数据,然后读到了修改后的数据,这个数据就是脏数据,依据脏数据所做的操作也是不正确的。例如 T1 开启了一个长事务,在较早的时间删除了一条记录,此时事务 T2 正好要统计表中记录总数,会少统计一条,随后 T1 事务发现删错了,进行了回滚操作,再随后 T2 事务再次统计,发现前后数据不一致。这显然违背了隔离性原则。
简单来说,脏读是指读到了未持久化的数据。
不可重复读
在同一事务中,多次读取同一数据返回的结果有所不同。换句话说,后续读取读到的是另一事务已提交的更新数据。例如 T1 开启了一个较长的事务,在较早的时间读取了一个数据,在中间另一个事务 T2 更改了数据并提交,随后 T1 再次读取但获得了新版本的数据,这时在同一个事物中多次读取同一数据却得到不同结果,这种现象就是不可重复度。显然,这也不符合隔离性原则。
幻读
在同一个事物中,以同样的条件进行范围查询,两次获得的记录数不一样。事务 T1 先执行了一次查询,然后事务 T2 新插入一行记录,接着 T1 使用相同的查询再次对表进行检索时,会发现这条出来的记录。这突然出现的记录就如一个“幻像”。 与“不可重复读”不同的是,幻读专指新插入的行。
我们要对事务进行隔离。事务隔离分为四个级别:
未提交读(READ-UNCOMMITED)
该级别的隔离性最弱,但并发性最好;事务中的修改,即使没有提交,对其他事务也都是可见的。也就是说事务可以读取未提交的数据,即产生脏读现象。
提交读(READ COMMITED)
一个事务提交后,它的变更才能被其他事务看到。大多数据库系统的默认级别,但 MySQL 不是。该级别的隔离可杜绝脏读,但仍会发生不可重复读。
可重复读(REPEATABLE READ)
MySQL 中事务的默认隔离级别。与“不可重复读”相反**,“可重复读”是指在同一事务中多次读取同一数据时得到的都是事物开始那个时间点的数据版本**,也可以理解为事务开始时对数据库拍了一个快照,后续操作都是在快照基础上进行的,自然不会读到别的事务的提交结果。
该级别直接针对不可重复读,但不能解决幻读。
可串行化(SERIALIZABLE)
当两个事务间存在读写冲突时,数据库通过加锁强制事务串行执行,解决了前面说的所有问题(脏读、不可重复读、幻读)。是最高的隔离级别。
名称 | 含义 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
未提交读 | 一个事务提交前,它的变更就已经能被其他事务看到 | √ | √ | √ |
读提交 | 一个事务提交后,它的变更才能被其他事务看到 | × | √ | √ |
可重复读 | 未提交的事务的变更不能被其他事务看到,同时一次事务过程中多次读取同样记录的结果是一致的。 | × | × | √ |
可串行化 | 当两个事务间存在读写冲突时,数据库通过加锁强制事务串行执行 | × | × | × |
数据定义语言 DDL
表的设计原则
表是数据库中最重要的对象,是数据库的基本存储单元,由行(记录)和列(字段)组成。 表名和 Java 语言中的变量名一样,有自己的命名规则:
- 英文字母、数字和下划线
_
组成,命名简洁明确,多个单词用下划线_
连接; - 英文全部使用小写,禁止出现大写;
- 禁止使用数据库关键字,如:name,time ,datetime,password 等;
- 表名称不应该取得太长(一般不超过三个英文单词);
- 表的名称一般使用名词或者动宾短语;
- 使用英语名词时,用单数形式表示名称,而不用复数,例如,使用 employee 表示员工表,而不是 employees。
设计表时应遵循一个基本原则:只将实体的直接属性纳入。举例说明:已考虑公民表有姓名、年龄、联系方式、国籍四个字段,国籍对应国家,那么国家信息(如名称、代码、语言等)是否该放入这个表?
一种考虑是,直接在该表中增加国家信息的多个字段,该表中既有个人信息,也有国家信息。但这会产生一个问题,以中国为例,该表至少有 13 亿条记录,个人直接属性是不同的,但国家信息部分会重复 13 亿次,造成了存储空间的极大浪费。
第二种方法就是拆分,把个人信息和国家信息拆分成两个表。如此国家信息表中只需要一条记录就可以表示中国的信息。在此基础上,利用我们前学习的连接查询可同时得到个人信息和国家信息。
单表的字段设计,多表的关系设计,这些都需要数据库设计范式的指导。
关系型数据库的第一范式
关系型数据库目前有 6 种范式,数据库设计好坏的判断标准就是看它满足了第几范式。通过企业项目对数据库的使用来看,一般情况下我们只要满足前三个范式即可
第一范式(1NF)指在关系模型中,数据库表的每一列都是不可分割的原子数据项。即实体中的某个属性有多个值时,必须拆分为多个属性。
学号 | 姓名 | 专业/学院 | 课程名 | 分数 |
---|---|---|---|---|
1 | 小张 | 大数据专业/计算机学院 | HTML | 98 |
1 | 小张 | 大数据专业/计算机学院 | JAVA | 95 |
2 | 小李 | 大数据专业/计算机学院 | Python | 100 |
2 | 小李 | 大数据专业/计算机学院 | MySQL | 90 |
3 | 小王 | 金融管理专业/财经学院 | 经济学 | 85 |
在上表中,“专业/学院”这个字段包含了专业和学院两个值,根据 1NF 的要求,必须把“专业/学院”拆分为“专业”和“学院”两个字段。拆分后的表如下表所示:
学号 | 姓名 | 专业 | 学院 | 课程名 | 分数 |
---|---|---|---|---|---|
001 | 小张 | 大数据专业 | 计算机学院 | HTML | 98 |
001 | 小张 | 大数据专业 | 计算机学院 | JAVA | 95 |
002 | 小李 | 大数据专业 | 计算机学院 | Python | 100 |
002 | 小李 | 大数据专业 | 计算机学院 | MySQL | 90 |
003 | 小王 | 金融管理专业 | 财经学院 | Linux | 85 |
此时该表符合了第一范式。
关系型数据库的第二范式
在 1NF 的基础上,第二范式(2NF)要求表必须有主码,非码属性必须完全依赖于主码。要完全理解 2NF 的含义,需要先弄清楚几个概念:
-
函数依赖:设有属性 A、B,如果通过 A 属性(或属性组)的值可以确定唯一 B 属性的值,则可以称为 B 依赖 A 或者 A 决定 B,用 “->” 来表示决定(依赖)关系,记作 A->B。例如,可以通过身份证号来确定学生姓名。
函数依赖又分为三种:分别是完全函数依赖、部分函数依赖、传递函数依赖。
- 完全函数依赖:如果 A 是一个属性组(由多个属性组成),则 B 属性值的确定需要依赖 A 属性组中的所有属性值。例如:把学号和课程名作为属性组,分数的确定就必须要同时知道学号和课程名才行。少了学号,不知道是谁的成绩;少了课程名,只知道谁的成绩而不知道是哪门课的成绩。所以该属性组的两个值必不可少,这就是完全函数依赖。
- 部分函数依赖:如果 A 是一个属性组,则 B 属性值的确定只需要依赖 A 属性组中的部分属性值。例如:把学号和课程名作为属性组,姓名的确定只需要 A 中的学号即可,和课程名无关。这就是部分函数依赖。
- 传递函数依赖:即依赖的传递关系,通过 A 可以确定 B,记作 A->B;通过 B 可以确定 C,记作 B->C;可得出 A->C;这就是传递依赖关系。
-
候选码:如果表中,一个属性或属性组,被其它所有属性完全函数依赖,则称这个属性或属性组为该表的候选码,简称码。成绩表中有学号、课程名、分数三个属性,分数的确定完全依赖学号和课程名,所以学号和课程名组成的属性组就是该表的候选码。
-
主属性码:主属性码也叫主码,在多个候选码中挑选一个做主码,也即是我们常说的主键。
-
非主属性码:除主属性码以外,其余的叫做非主属性码。
理解了这些概念以后,再来判断上表是否符合 2NF 的标准。
步骤如下:
- 找出数据表中所有的候选码;
- 根据候选码,找出主属性码;
- 得到非主属性码;
- 查看非主属性码对主属性码是否完全依赖(不存在部分函数依赖)。
具体实现:
第一步:
- 查看每一个单一属性,当它的值确定了,剩下的所有属性值是否都能确定。该表中单一属性都没法确定其它属性的值。如:以学号为主键,对应的课程名却出现了多个,所以学号不是候选码。
- 查看所有两两属性的属性组,当属性组确认后,剩下的所有属性值是否都能确定。
依次类推,最后得到该表的候选码只有一个,即(学号,课程名)。
第二步:
因为候选码只有一个,所有主码也就确定了。
第三步:
非主属性码就是(姓名,专业,学院,分数)
第四步:
判定非主属性码是否部分函数依赖主码?
对于主码(学号,课程名)->姓名,只需要学号即可确定姓名,所以存在非主属性码姓名,对主码(学号,课程名)的部分函数依赖。此时我们可以判定之前的表不满足 2NF。
为了让之前的表满足 2NF,我们需要消除表中部分函数依赖,办法只有一个,那就是拆分表。将之前的表拆分为两个表。一个叫做选课表,包含的属性有学号、课程名、分数;另一个叫做学生信息表,包含的属性有学号、姓名、专业、学院,如下选课表和学生信息表所示。
选课表
学号 | 课程名 | 分数 |
---|---|---|
001 | HTML | 98 |
001 | JAVA | 95 |
002 | Python | 100 |
002 | MySQL | 90 |
003 | Linux | 85 |
学生信息表
学号 | 姓名 | 专业 | 学院 |
---|---|---|---|
001 | 小张 | 大数据专业 | 计算机学院 |
002 | 小李 | 大数据专业 | 计算机学院 |
003 | 小王 | 金融管理专业 | 财经学院 |
对于选课表,学号和课程名是主码,唯一的非主属性分数对主码完全函数依赖。所以该表符合 2NF;对于表 学生信息表,学号是主码,该码只有一个属性,所以不存在非主属性对码的部分函数依赖,符合 2NF。
达到 2NF 还会出现什么问题呢?请看第三范式。
关系型数据库的第三范式
在 2NF 的基础上,第三范式(3NF)要求不存在传递依赖。也即是,如果存在非主属性码对于码存在传递函数依赖,则不符合 3NF。
如学生信息表,主码为学号,非主属性码为姓名、专业、学院。因为学号->专业,专业->学院,所以学生信息表存在传递函数依赖,不符合 3NF。为此我们需要把学生信息表拆分成两个表,如学生专业表与专业信息表。
学生专业表
学号 | 姓名 | 专业 |
---|---|---|
001 | 小张 | 大数据专业 |
002 | 小李 | 大数据专业 |
003 | 小王 | 金融管理专业 |
专业信息表
专业 | 学院 |
---|---|
大数据专业 | 计算机学院 |
金融管理专业 | 财经学院 |
拆分以后,当再想删除某个专业的所有学生信息的时候,专业信息不会一起被删除。满足 3NF 后,我们所设计的表就具有较好的规范了,同时降低了数据冗余。
如果创建表
创建表使用的 SQL 语句是 CREATE TABLE,基本语法形式如下:
CREATE TABLE TABLE
(colname1 type1, colname2 type2 … colnamen typen)
其中,table 表示表名,colname1,colname2,… 表示表的字段名,type1,type2,… 是字段的数据类型。
进入 MySQL 客户端,切换到 world 数据库,执行如下语句创建 student 表:
CREATE TABLE student (
student_id INT,
student_name VARCHAR(20),
student_specialty VARCHAR(20)
);
输出结果:
提醒:SQL 语句默认以 ;
表示结束,上述代码虽然写了 5 行,但是仅表示一条语句;每个字段与字段类型之间用空格隔开,字段与字段之间用 ,
分割,最后一个字段的类型后面可以不加 ,
。
执行如下命令查看当前数据库 world 中有哪些数据表:
SHOW TABLES;
输出结果:
如上图所示,可以看到新增加了刚刚创建的 student 数据表。
可以使用 DESC
命令查看某个表的结构:
DESC student;
输出结果:
我们目前只关心 Field 和 Type 两列即可,后面的四列我们会在下一节 MySQL 约束控制中介绍。
DESC 是 DESCRIBE 的简写,大家可以尝试执行 DESCRIBE student
语句。
细心的同学们可能已经发现,我们将 student 表中的 student_id 定义为 INT ,没有指定长度时,默认长度是 11 位。
如何修改表
如果我们需要对表结构进行更改,比如添加或者删除一个字段,或者重命名某个字段等等。在改动不大的前提下,可以在原表的基础上进行修改。
修改表使用的 SQL 语句是 ALTER TABLE
,有 3 种形式,分别是修改字段、添加字段、删除字段。
其中修改字段分两种:改字段名和不改字段名。
-
改字段名,字段类型可以改也可以不改,其 SQL 语法如下:
ALTER TABLE table CHANGE oldcolname newcolname type;
-
不改字段名而只修改字段类型,其 SQL 语法如下:
ALTER TABLE tableName MODIFY colname newtype;
-
添加字段语法如下:
ALTER TABLE tableName ADD colname type;
-
删除字段语法如下:
ALTER TABLE tableName DROP colname;
-
增加一个名为 age 的年龄字段,类型为 INT,实现的 SQL 语句如下:
ALTER TABLE student ADD age INT;
输出结果:
使用
DESC
命令查看表结构的变化:DESC student;
输出结果:
-
修改表字段,原属性都是以 student 为前缀。为保持命名风格的统一,现修改 age 字段为 student_ age,实现的 SQL 语句如下:
ALTER TABLE student CHANGE age student_age INT;
使用
DESC
命令查看表结构的变化:DESC student;
输出结果:
使用 MODIFY 修改字段的数据类型:
ALTER TABLE student MODIFY student_age FLOAT;
使用
DESC
命令查看表结构的变化:DESC student;
输出结果:
-
删除字段则比较简单,在 DROP 后面跟上字段名即可,不用指出字段类型。现把添加的 student_age 字段给删除,其 SQL 语句如下:
ALTER TABLE student DROP student_age;
使用
DESC
命令查看表结构的变化:DESC student;
输出结果:
需要注意:“修改表”操作虽然是 SQL 99/92 中定义的标准语法,但各个关系型数据库在实现方面仍存在一定的语法差异。
删除表 DROP 和 DELETE 的区别
在 MySQL 中,表的删除操作有三种,这里我们先说两种。分别是利用 DROP TABLE
语句实现表的删除,以及前面实验中提过的 DELETE FROM table
。
两者的区别在于:
DROP TABLE
不仅仅删除表的内容、而且会删除表的结构并释放空间。通俗的讲就是整个表以及表中数据全部删除,想再次操作这张表已经不可能,只有重新去创建一个。DELETE FROM table
删除的是表中的数据,就是清空表,表结构本身不变。系统需要一行一行的去删除数据,效率低下。
登录 MySQL,在 world 数据库中创建一张 student 表,并向 student 数据表中写入一条数据并查询表中的数据:
use world;
CREATE TABLE student (
student_id INT,
student_name VARCHAR(20),
student_specialty VARCHAR(20)
);
INSERT INTO student VALUES (1, 'Monkey', 'Eat');
SELECT * FROM student;
部分操作截图如下:
现在执行 DELETE 语句清空表:
DELETE FROM student;
输出结果:
使用 SHOW TABLES
命令查看数据表的列表:
SHOW TABLES;
输出结果:
结果显示 student 表还在,然后查看表中数据:
SELECT * FROM student;
输出结果:
说明 DELETE FROM
语句确实只删除了数据。接下来使用 DROP TABLE
对表进行删除操作,其 SQL 语句如下:
DROP TABLE student;
输出结果:
使用 SHOW TABLES
命令查看现有数据表:
SHOW TABLES;
输出结果:
结果显示表已经删除,数据也丢失。
如何截断表
截断表的作用和 DELETE FROM
语句类似,删除表中所有的数据。截断表使用的语句是 TRUNCATE TABLE
。它和 DELETE FROM
区别在于,TRUNCATE TABLE
语句是数据定义语句,提交后不会产生回滚信息,所以它的速度更快。
其语法如下:
TRUNCATE TABLE table;
在 MySQL 客户端执行如下语句新建一张 student 表,并向表中添加一些数据:
CREATE TABLE student (
student_id INT,
student_name VARCHAR(20),
student_specialty VARCHAR(20)
);
INSERT INTO student VALUES
(1, '小章', '大数据专业'),
(2, '小李', '大数据专业'),
(3, '小蓝', '计算机专业');
表中数据如下结果所示:
SELECT * FROM student;
输出结果:
使用 TRUNCATE TABLE
命令清空 student 数据表:
TRUNCATE TABLE student;
查看表中数据:
SELECT * FROM student;
输出结果:
可以看出此时表仍然存在,和 DELETE FROM
实现的效果差不多,真正的差别只有当我们要清空的表数据量庞大的时候才能体现出来