第5 章 使用SQL 管理表数据 对数据库中表数据执行添加、删除、修改和查询操作是必不可少的工作。查询是指从 数据库中获取用户所需要的数据,查询操作在数据库操作中经常用到,而且也是最重要的 操作之一。添加是向数据库表中添加不存在的记录,修改是对已经存在的记录进行更新, 删除则是删除数据库中已存在的记录。 查询数据库中的记录有多种方式,可以查询所有的数据,也可以根据用户自己的需 要进行查询,还可以借助集合函数进行查询。通过不同的查询方式,可以获取不同的 数据。 5.1 SQL的数据操纵功能 SQL语言的数据操纵语句DML主要包括插入数据、修改数据和删除数据三种语句。 5.1.1 插入数据记录 插入数据是把新的记录插入到一个已有表中。插入数据使用语句INSERTINTO, 可分为以下几种情况。 1.插入一行新记录 语法格式为: INSERT INTO<表名>[(<列名1>[,<列名2>…])]VALUES(<值>) 其中: . <表名>是指要插入新记录的表。 . <列名>是可选项,指定待添加数据的列,列出列名,则VALUES子句中值的排 列顺序必须和列名表中的列名排列顺序一致,个数相等,数据类型一一对应;若省 略列名,则VALUES子句中值的排列顺序必须和定义表时的列名排列顺序一致, 个数相等,数据类型一一对应。 . VALUES子句指定待添加数据的具体值。 【例5-1】 在student表中插入一条学生记录(学号:2' 02011070339',姓名:梦欣怡, 性别:女,出生年月:2002-06-18,专业号:1102,所在班:大数据2001)。 INSERT INTO student VALUES ('202011070339', '梦欣怡', '女', '2002-06-18', '1102', '大数据2001'); 第5 章 使用SQL 管理表数据 95 注意: . 必须用逗号将各个数据分隔开,字符型数据要用单引号括起来。 .INTO 子句中没有指定列名,则新插入的记录必须在每个属性列上均有值,且 VALUES子句中值的排列顺序要和表中各属性列的排列顺序一致。 2.插入一行的部分数据值 【例5-2】 在sc表中插入一条选课记录(2' 02011070339',5' 8130540')。 INSERT INTO sc(Sno, Cno) VALUES ('202011070339 ', '58130540'); 语句将VALUES子句中的值按照INTO 子句中指定列名的顺序插入到表中。 对于INTO 子句中没有出现的列,新插入的记录在这些列上将取空值,如sc表中的 grade列即赋空值(NULL)。 但对于那些在表定义时有NOTNULL约束的属性列,则不能取空值。 3.插入多行记录 用户可以从一个表中抽取数据插入另一表中,这可通过子查询来实现。 插入数据的命令语法格式为: INSERT INT0<表名>[(<列名1>[,<列名2>…])] 子查询 【例5-3】 建一点名表studentlist(Sno,Sname,Ssex),其中字段含义分别是学生学 号,学生姓名,学生性别,并把学生表中的相关数据插入到点名表中。 CREATE TABLE studentlist ( Sno CHAR(10), Sname VARCHAR(20), Ssex VARCHAR(10) ); INSERT INTO studentlist(Sno,Sname,Ssex) SELECT Sno,Sname,Ssex FROM student; 5.1.2 修改数据记录 SQL语言可以使用UPDATE 语句对表中的一行或多行记录的某些列的值进行修 改,其语法格式为: UPDATE<表名> SET<列名>=<表达式>[,<列名>=<表达式>…] [WHERE<条件>] 其中: . <表名>:是指要修改的表。 . SET子句:给出要修改的列及其修改后的值。 . WHERE子句指定待修改的记录应当满足的条件,WHERE子句省略时,则修改 96 数据库技术与应用(MySQL 版)(第2 版) 表中的所有记录。 下面的示例修改一行记录。 【例5-4】 把转专业的“郭爽”同学从“供应链2001”转到“区块链2001”。 UPDATE student SET Sclass='区块链2001' WHERE Sname='郭爽'; 下面的示例修改多行记录。 【例5-5】 将所有课程的学分增加1。 UPDATE course SET Ccredit=Ccredit+1; 【例5-6】 把选修表中每个同学的成绩提高5分。 UPDATE sc SET Grade=Grade+5; 5.1.3 删除数据记录 使用DELETE语句可以删除表中的一行或多行记录,其语法格式为: DELETE FROM<表名> [WHERE<条件>] 其中: . <表名>:要删除数据的表。 . WHERE子句:指定待删除的记录应当满足的条件,WHERE子句省略时,则删 除表中的所有记录。 下面的示例删除一行记录。 【例5-7】 删除“马琦”同学的记录。 DELETE FROM student WHERE Sname='马琦'; 下面的示例删除多行记录。 【例5-8】 从学生表中删除所有工商1401班的同学记录。 DELETE FROM student WHERE Sclass='工商1401'; 【例5-9】 删除学生表中的所有记录。 DELETE FROM student; 执行此语句后,student表即为一个空表,但其定义仍存在于数据字典中。 5.1.4 使用TRUNCATE 清空表数据 TRUNCATE用于完全清空一个表,基本语法格式如下: TRUNCATE[table]表名 第5 章 使用SQL 管理表数据 97 【例5-10】 清除sc表。 TRUNCATE table sc; 注意:TRUNCATETABLE 与DELETE的区别如下。 . TRUNCATETABLE在功能上与不带WHERE 子句的DELETE 语句相同:两 者均删除表中的全部行。但TRUNCATETABLE 比DELETE 速度快,且使用 的系统和事务日志资源少。DELETE 语句每次删除一行,会在事务日志中为所 删除的每行记录一项。 . TRUNCATETABLE通过释放存储表数据所用的数据页来删除数据,且只在事 务日志中记录页的释放。 TRUNCATE、DELETE、DROP的比较如下。 . TRUNCATETABLE:删除内容、释放空间但不删除定义。 . DELETETABLE:删除内容不删除定义,不释放空间。 . DROPTABLE:删除内容和定义,释放空间。 5.2 SQL的数据查询功能 查询功能是SQL语言的核心功能,是数据库中使用最多的操作,查询语句也是最复 杂的一个语句。本节中的例子都是基于jxgl数据库的,数据表中的数据请参见5.3节。 5.2.1 查询语句SELECT 的基本结构 SELECT语句可以有效地从数据库的表或视图中访问和提取数据,并具有强大的单 表和多表查询功能,正因为如此,SELECT语句的可选项很多,语法也比较复杂。其一般 格式为: SELECT[ALL|DISTINCT]<目标列表达式>[[AS]<新列名>][,…n] FROM<表名或视图名>[[AS]<别名>][…n] [WHERE<条件表达式>] [GROUP BY<分组依据列>] [HAVING<条件表达式>] [ORDER BY<排序依据列>[ASC|DESC]][,…n] [LIMIT N,M] 其中: . [ALL|DISTINCT]:指定在结果集中是否显示重复行。ALL表示显示,为默认 值;DISTINCT表示不显示。 . <目标列表达式>[[AS]<新列名>][,…n]:指定为结果集选定的列。特别地, 如果该处为“*”,则表示输出所有列。 . <表名或视图名>[[AS]<别名>][,…n]:指定从其中检索数据的表或视图。 . [WHERE<条件表达式>]:指定数据检索的条件。 98 数据库技术与应用(MySQL 版)(第2 版) . [GROUPBY<分组依据列>]:实现对数据的分组查询。 . [HAVING<条件表达式>]:用于分组后的筛选条件。 . [ORDERBY<排序依据列>[ASC|DESC]][,…n]:对结果集按<排序依据列 >指定的列的值排序。其中ASC 表示升序排列,为默认值;DESC 表示降序 排列。 . [LIMITN,M]:表示只从查询结果集中输出从N 到M 行。 整个SELECT语句的含义是:根据WHERE子句的条件表达式,从FROM 子句指 定的基本表或视图中找出满足条件的元组,再按SELECT 子句中的目标列表达式,选出 元组中的属性值,形成结果表。如果有GROUP子句,则将结果按<分组依据列>的值 进行分组,该属性列的值相等的元组为一个组,每个组产生结果表中的一条记录。通常会 在分组中使用集函数。如果GROUP子句带HAVING 短语,则只有满足指定条件的组 才予以输出。如果有ORDER 子句,则还需对结果按<排序依据列>的值升序或降序 排列。 5.2.2 单表查询 1.选择表中的若干列 下面的示例查询全部列。 【例5-11】 查询全体学生的详细记录。 SELECT * FROM student; 该SELECT语句实际上是无条件地把student表的全部信息都查询出来,所以也称 为全表查询,这是很常用的也是最简单的一种查询。 输出结果如图5-1所示。 图5-1 查询全体学生详细记录的结果 第5 章 使用SQL 管理表数据 99 下面的示例查询指定列。 【例5-12】 查询全体学生的学号与姓名。 SELECT Sno,Sname FROM student; 输出结果如图5-2所示。 目标列表达式中各个列的先后顺序可以与表中的顺序不一致。也就是说,用户在查 询时可以根据应用的需要改变列的显示顺序。 【例5-13】 查询全体学生的姓名、学号、所在班级。 SELECT Sname,Sno,Sclass FROM student; 输出结果如图5-3所示。 图5-2 查询全体学生的学号 与姓名的结果 图5-3 查询全体学生的姓名、学号、 所在班级的结果 结果表中列的顺序与基表中不同,是按查询要求先列出姓名属性,再列出学号属性和 所在系属性。 下面示例查询经过计算的值。 SELECT子句的<目标列表达式>不仅可以是表中的属性列,也可以是有关表达 式,即可以将查询出来的属性列经过一定的计算后列出结果。 【例5-14】 查询全体学生的姓名及其年龄。 SELECT Sname,YEAR(now())-YEAR(Sbirth) FROM student; 本例中,<目标列表达式>中第二项不是通常的列名,而是一个计算表达式,是用当 前的年份减去学生的出生日期,这样所得的即为学生的年龄。其中,YEAR()是输出年份 的函数,now()是输出当前日期的函数。 1 00 数据库技术与应用(MySQL 版)(第2 版) 输出结果如图5-4所示。 表达式不仅可以是算术表达式,还可以是字符串常量、函数等。 【例5-15】 查询全体学生的姓名、出生年份。 SELECT Sname AS 学生姓名, YEAR(Sbirth) AS 出生年份FROM student; 输出结果如图5-5所示。 图5-4 查询全体学生的姓名 及其年龄的结果 图5-5 查询全体学生的姓名及其出生 年份(列取别名)的结果 从上例可以看出用户可以通过指定别名来改变查询结果的列标题,这对于含算术表 达式、常量、函数名的目标列表达式尤其有用。 2.选择表中的若干元组 下面的示例消除取值重复的行。 【例5-16】 查询所有选修过课的学生学号。 SELECT Sno FROM sc; 执行上面的SELECT语句后,输出结果如图5-6所示。 该查询结果里包含了许多重复行。如果想去掉结果表中的重复行,应使用 DISTINCT短语: SELECT DISTINCT Sno FROM sc; 输出结果如图5-7所示。 要查询满足条件的元组,可以通过WHERE子句实现。WHERE子句常用的查询条 件如表5-1所示。 第5 章 使用SQL 管理表数据1 01 图5-6 查询所有选修过课的 学生学号的结果 图5-7 查询所有选修过课的学生 学号(去除重复行的结果) 表5-1 常用的查询条件 查询条件谓 词 比较(比较运算符) =、>、>=、<、<=、<>(!=)、NOT 确定范围BETWEEN…AND、NOTBETWEEN…AND 确定集合IN、NOTIN 字符匹配LIKE、NOTLIKE 空值ISNULL、ISNOTNULL 多重条件AND、OR 3.比较大小 下面的示例比较大小。 【例5-17】 查询“大数据2001”班的全体学生名单。 SELECT Sname FROM student WHERE Sclass='大数据2001'; 输出结果如图5-8所示。 【例5-18】 查询所有“2001”年以前出生学生的姓名及其出生日期。 SELECT Sname,Sbirth FROM student WHERE Sbirth<'2001-01-01'; 输出结果如图5-9所示。 【例5-19】 查询考试成绩低于75的学生学号。 SELECT DISTINCT Sno FROM sc WHERE Grade<75; 这里使用了DISTINCT短语,会在结果集中去掉重复行。用在此处,使得当某个学 生有多门课程不及格时,其学号也只列一次。 1 02 数据库技术与应用(MySQL 版)(第2 版) 图5-8 查询“大数据2001”班的 全体学生名单的结果 图5-9 查询所有“2001”年以前出生学生 的姓名及其出生日期的结果 输出的结果如图5-10所示。 【例5-20】 查询在'2000-01-01' 和'2002-12-31'之间出生的学生的姓名、班级和出生 日期。 SELECT Sname,Sclass,sbirth FROM student WHERE sbirth BETWEEN '2000-01-01' AND '2002-12-31' ; 输出结果如图5-11所示。 图5-10 查询考试成绩低于75的 学生学号的结果 图5-11 查询在两个日期之间出生学生的 姓名、班级和出生日期的结果 与BETWEEN…AND…相对的谓词是NOTBETWEEN…AND…。 【例5-21】 查询不在2' 000-01-01' 和2' 002-12-31'之间出生的学生的姓名、班级和出生 日期。 SELECT Sname,Sclass,sbirth FROM student WHERE sbirth NOT BETWEEN '2000-01-01' AND '2002-12-31' ; 输出结果如图5-12所示。 【例5-22】 查询“区块链2001”和“供应链2001”班学生的姓名和性别。 SELECT Sname,Ssex FROM student WHERE Sclass IN('区块链2001','供应链2001'); 输出结果如图5-13所示。 第5 章 使用SQL 管理表数据1 03 图5-12 查询不在两个日期之间出生学生 的姓名、班级和出生日期的结果 图5-13 查询指定两个班级学生的 姓名和性别的结果 与IN 相对的谓词是NOTIN,用于查找属性值不属于指定集合的元组。 【例5-23】 查询既不是“区块链2001”也不是“供应链2001”班的学生的姓名和性别。 SELECT Sname, Ssex FROM student WHERE Sclass NOT IN('区块链2001','供应链2001'); 图5-14 查询不在指定两个班级中 的学生姓名和性别的结果 输出结果如图5-15所示。 谓词LIKE 可以用来进行字符串的匹配。其语 法格式如下: [NOT]LIKE '<匹配串>' 其含义是查找指定的属性列值与<匹配串>相 匹配的元组。 <匹配串>可以是一个完整的字符串,也可以含 有以下通配符: . %(百分号):代表任意长度(长度可以为0) 的字符串。 . _(下横线):代表任意单个字符。 . []:匹配[]中的任意一个字符。 . [^]:不匹配[]中的任意一个字符。 当<匹配串>中没有通配符时,“LIKE”的作用等同于“=”。 【例5-24】 查询所有姓李的学生的姓名、学号和性别。 SELECT Sname,Sno,Ssex FROM student WHERE Sname LIKE '李%'; 输出结果如图5-15所示。 【例5-25】 查询姓名中第二个字为“小”的学生的姓名和学号。 SELECT Sname,Sno FROM student WHERE Sname LIKE'_小%'; 输出结果如图5-16所示。 【例5-26】 查询所有不姓李的学生的姓名。 SELECT Sname FROM student WHERE Sname NOT LIKE'李%';