第 3 章 关系数据库语言———SQLSQL(StructuredQueryLanguage,结构查询语言)是1974 年由Boyce和 Chamberlin提出的,是在关系数据库中使用最普遍的语言,包括数据定义、数据查 询、数据操作和数据控制4种功能。本章主要介绍SQL 中的基本操作。 ..3.1 SQL 简介 SQL 是一个综合的、通用的、功能极强同时又简洁易学的语言。其主要特点如下。 SQL介绍 (1)综合统一。SQL 集数据定义语言(DataDefinitionLanguage,DDL )、数 据操纵语言(DataManipulationLanguage,DML )、数据控制语言(DataControl Language,DCL)的功能于一体,语言风格统一,可以独立完成数据库生命周期中 的全部活动,为数据库应用系统的开发提供了良好的环境。 (2)高度非过程化。SQL 是高度非过程化语言,当进行数据操作时,只要提 出“做什么”,而无须指明“怎么做”,用户不需要了解存取路径,存取路径的选择以 及SQL 语句的操作过程由系统自动完成。 (3)面向集合的操作方式。SQL 采用集合操作方式,其操作对象和查找结果 都是元组的集合。 (4)以同一种语法结构提供两种使用方式。SQL 既可以作为独立的语言,采 用联机交互的使用方式,用户在终端键盘上直接输入SQL 命令对数据库进行操 作,也可以采用嵌入式,嵌入高级语言(如C# 、Java)程序中,供程序员设计程序时 使用。两种不同的使用方式中,SQL 的语法结构基本上是一致的,提供了极大的 灵活性和方便性。 (5)语言简洁,易学易用。SQL 功能极强,但十分简洁,完成核心功能只用了 9个动词,如表3. 1所示。 表3. 1 SQL的动词 SQL功能动词 数据查询SELECT 数据定义CREATE,DROP,ALTER 数据操纵INSERT,DELETE,UPDATE 数据控制GRANT,REVOKE 48 数据库原理与应用(微课版) SQL由以下几个部分组成。 (1)数据定义语言。SQLDDL提供定义关系模式和视图、删除关系和视图、修改关系 模式的命令。 (2)交互式数据操纵语言。SQLDML提供查询、插入、删除和修改的命令。 (3)完整性控制。SQLDDL包括定义数据库中的数据必须满足的完整性约束条件的 命令,对于破坏完整性约束条件的更新将被禁止。 (4)安全性控制。SQLDDL中包括说明对关系和视图的访问权限。 (5)事务控制。SQL提供定义事务开始和结束的命令。 (6)嵌入式SQL和动态SQL。用于嵌入某种通用的高级语言(C,C++,Java等)中混 合编程。其中,SQL负责操纵数据库,高级语言负责控制程序流程。 .. 3.2 数据定义 SQL支持关系数据库的三级模式结构,如图3.1所示。其中,外模式对应于视图,模式 对应于基本表,内模式对应于物理存储文件。 图3.1 SQL支持的数据库模式 SQL的数据定义功能包括定义基本表、定义视图、定义索引。除此之外,SQL还可以定 义数据库、定义存储过程等。 3.2.1 基本表的定义 1.基本表的创建 创建基本表的一般格式为 CREATE TABLE <表名> (<列名> <数据类型> [<列级完整性约束条件>] [,<列名> <数据类型> [<列级完整性约束条件>]] … [,<表级完整性约束条件>]); 说明: (1)定义中()为语法结构,不可省略;< >括号内的内容为表定义的必选项;[]括号 内的内容为表定义的可选项。 (2)数据类型定义属性列的数据类型,不同数据库产品在数据类型的种类和关键词上 第3章关系数据库语言———SQL49 存在差异。 (3)如果完整性约束条件涉及该表的多个属性列,则必须定义表级完整性约束条件,否 则既可以定义列级完整性约束条件,也可以定义表级完整性约束条件。 2.属性的数据类型 当用SQL语句定义表时,需要为表中的每一个属性定义数据类型及长度。 PostgreSQL的常用数据类型如表3.2所示。 表3.2PostgreSQL的数据类型 数据类型含义 Smallint 短整型(2B) Integer 整型(4B) Bigint 大整型(8B) Decimal 用户给定精度的浮点型 Real 单精度浮点型(4B) Doubleprecision 双精度浮点型(8B) Char(n) 固定长度字符型,表示n个字符的固定长度字符串 Varchar(n) 可变长度的字符串,表示最多可以有n个字符的字符串 text 没有限制的可变长度的字符串 Bytea 1B或4B的二进制串 Date 日期类型 Time 时间类型 Timestamp 时间戳类型 Money 货币类型 Boolean 布尔型 3.完整性约束条件 常用的约束子句如下。 (1)PRIMARYKEY约束。PRIMARYKEY子句用来定义表的主码,一个表只能包 含一个PRIMARYKEY约束,如果表的主码由多个属性构成,需要在表级的完整性约束上 定义;如果主码由单个属性构成,则既可在列级定义也可在表级定义。 (2)NOTNULL或NULL约束。用关键词NOTNULL或NULL说明指定属性的属 性值是否允许为空值。空值是关系数据库的一个重要概念,表示不确定或没有意义,与空串 或0等具有不同的含义。 (3)UNIQUE约束。用UNIQUE约束定义属性的属性取值必须是唯一的。 (4)FOREIGNKEY…REFERENCES约束。用FOREIGNKEY…REFERENCES约 束来定义参照完整性,因为涉及两个表中的属性,该约束必须定义在表级完整性上。 【例3.学生成绩管理数据库有三个基本表:学生表Student,课程表Course,学生选 1】 50 数据库原理与应用(微课版) 课表SC。 学生表:Student(Sno,Sname,Ssex,Sage,Sdept) 课程表:Course(Cno,Cname,Cpno,Ccredit) 学生选课表:SC (Sno,Cno,Grade) 关系的主码用下画线表示。各个表中的数据如图3.2所示。在PostgreSQL数据库中 创建学生成绩管理数据库。 图3.2 学生成绩管理数据库的数据示例 CREATE TABLE Student( Sno CHAR(12) PRIMARY KEY, Sname VARCHAR(8), Ssex CHAR(2) NOT NULL CHECK(Ssex IN ('男' , '女')), /*性别只能取'男'或'女'*/ Sage INT, Sdept VARCHAR(20)); CREATE TABLE Course( Cno CHAR(4) PRIMARY KEY, Cname CHAR(40), Cpno CHAR(4) , 第3章 关系数据库语言———SQL 51 Ccredit SMALLINT, FOREIGN KEY (Cpno) REFERENCES Course(Cno) ); CREATE TABLE SC( Sno CHAR(9), Cno CHAR(4), Grade SMALLINT, PRIMARY KEY (Sno,Cno), /* 主码由两个属性构成,必须作为表级完整性进行定义*/ FOREIGN KEY (Sno) REFERENCES Student(Sno), /* 表级完整性约束条件,Sno 是外码,被参照表是Student */ FOREIGN KEY (Cno) REFERENCES Course(Cno) /* 表级完整性约束条件,Cno 是外码,被参照表是Course*/ ); 3.2.2 基本表的修改 基本表在创建之后可以用SQL的ALTERTABLE命令修改表结构,该命令的一般格 式为 ALTER TABLE <表名> [ ADD <新列名> <数据类型> [ <列级完整性约束> ] ] [ DROP <完整性约束名> ] [ ALTER COLUMN <列名> <数据类型> ]; 说明: (1)ADD子句用于增加新的属性和该属性上的完整性约束。 (2)DROP 子句用于删除指定的完整性约束条件。 (3)ALTERCOLUMN 用于修改原有属性的数据类型。 【例3.2】 向Student表增加“生源地”列,其数据类型为字符型。 ALTER TABLE Student ADD Saddress VARCHAR(20); 不论基本表中原来是否已有数据,新增加的列一律为空值。 【例3.3】 将年龄的数据类型由整型改为短整型。 ALTER TABLE Student ALTER COLUMN Sage TYPE SMALLINT; 【例3.4】 增加课程表中对成绩的约束,使Grade为0~100。 ALTER TABLE SC ADD CHECK (Grade BETWEEN 0 AND 100); 3.2.3 基本表的删除 删除表命令的一般格式为 DROP TABLE <表名> {RESTRICT | CASCADE} 52 数据库原理与应用(微课版) 说明: (1)RESTRICT:删除表是有限制的。如果删除的基本表被其他的表引用或存在依赖 该表的对象,则此表不能被删除。 (2)CASCADE:删除该表没有限制。在删除基本表的同时,相关的依赖对象一起 删除。 .. 3.3 数据查询 数据库查询是数据库应用的核心内容,SQL提供查询语句SELECT,格式如下。 SELECT [ALL|DISTINCT] <目标列表达式> [, <目标列表达式>] … FROM <表名或视图名>[, <表名或视图名> ] … [ WHERE <条件表达式> ] [ GROUP BY <列名1> [ HAVING <条件表达式> ] ] [ ORDER BY <列名2 > [ ASC|DESC ] … ]; SQL查询子句的顺序为SELECT、FROM、WHERE、GROUPBY、HAVING 和ORDER BY。其中,SELECT、FROM 是必需的。具体说明如下。 (1)SELECT子句用来列出查询结果的属性,其输出可以是列名、表达式、聚集函数等; DISTINCT选项将去掉查询结果中重复的元组,ALL则保留查询结果中的重复元组,默认 情况下为ALL。 (2)FROM 子句指定查询的输入,可以是基本表,也可以是视图,或者是嵌套的子查询。 (3)WHERE子句用于对查询输出进行限定或是设置输入对象之间的连接条件等。 (4)GROUPBY子句用于对查询结果进行分组,可以利用它进行分组统计。 (5)HAVING 子句是对分组结果进行的限定条件,必须搭配GROUPBY 子句才能 出现。 (6)ORDERBY子句是对查询结果进行排序,并不改变数据本身的物理存储。 SELECT语句可完成简单的查询,也可完成复杂的连接查询及嵌套子查询。 3.3.1 单表查询 单表查询是仅涉及一个表的查询。 1.选择表中的若干列 (1)查询输出指定列。 【例3.5】 查询全体学生的学号与姓名。 SELECT Sno,Sname FROM Student; 【例3.6】 查询全体学生的姓名、学号、所在系。 SELECT Sname,Sno,Sdept FROM Student; 第3章 关系数据库语言———SQL 53 (2)查询输出所有属性列。 在SELECT关键字后面列出所有列名或者将<目标列表达式>指定为*。 【例3.7】 查询全体学生的详细记录。 SELECT Sno, Sname, Ssex, Sage, Sdept FROM Student; 或 SELECT * FROM Student; (3)查询输出经过计算的属性列。 SELECT子句的<目标列表达式>可以是算术表达式、函数或别名。 【例3.8】 查询全体学生的姓名及出生年份。 SELECT Sname, 2021-Sage /*假定当年的年份为2021 年*/ FROM Student; 【例3.9】 查询全体学生的姓名、出生年份和所有系,要求用小写字母表示所有系名。 SELECT Sname,2004-Sage "Year of Birth;", lower (Sdept) FROM Student; (4)更改列标题。 用户可以通过指定别名来更改查询结果的列标题,这可以使含有计算表达式、常量、函 数名的目标列表达式等的输出更简洁。 SELECT Sno 学号, Sname 姓名, 2021-Sage 出生年月 FROM Student; (5)去掉重复行。 SELECT子句可使用ALL或DISTINCT选项来显示符合查询条件的所有行或对符合 条件的行中去掉重复行,默认为ALL,使用DISTINCT则对重复出现的行只保留一行。 【例3.10】 查询全部的系。 SELECT Sdept 系FROM Student; 等价于: SELECT ALL Sdept FROM Student; 执行结果为 54 数据库原理与应用(微课版) 系 计算机系 数学系 计算机系 使用DISTINCT去掉结果表中的重复行: SELECT DISTINCT Sdept FROM STUDENT; 执行结果为 系 计算机系 数学系 2.带条件的查询 WHERE子句用于设置查询条件,过滤掉不需要的数据行,只有满足条件的行才出现 在查询结果中。常用的查询条件如表3.3所示。 表3.3 WHERE常用的查询条件 查询方式运 算 符 比较=、>、>=、<、<=、<> 确定范围BETWEENAND、NOTBETWEENAND 确定集合IN、NOTIN 字符匹配LIKE、NOTLIKE 空值ISNULL、ISNOTNULL 多重条件(逻辑运算) NOT、AND、OR (1)比较运算符。 用于进行比较的运算符一般包括=(等于)、>(大于)、>=(大于或等于)、<(小于)、 <=(小于或等于)、<>(不等于)。 【例3.11】 查询计算机系全体学生的名单。 SELECT Sname FROM Student WHERE Sdept='计算机系'; 【例3.12】 查询所有年龄在20岁以下的学生姓名及其年龄。 SELECT Sname, Sage FROM Student WHERE Sage < 20; 第3章 关系数据库语言———SQL 55 (2)确定范围。 【例3.13】 查询年龄为20~23岁(包括20岁和23岁)的学生的姓名、系别和年龄。 SELECT Sname, Sdept, Sage FROM Student WHERE Sage BETWEEN 20 AND 23; 【例3.14】 查询年龄不为20~23岁的学生姓名、系别和年龄。 SELECT Sname, Sdept, Sage FROM Student WHERE Sage NOT BETWEEN 20 AND 23; (3)确定集合。 【例3.15】 查询信息系(IS)、数学系(MA)和计算机系(CS)学生的姓名和性别。 SELECT Sname, Ssex FROM Student WHERE Sdept IN ( 'IS', 'MA', 'CS' ); 【例3.16】 查询既不是信息系(IS)、数学系(MA),也不是计算机系(CS)的学生的姓名 和性别。 SELECT Sname, Ssex FROM Student WHERE Sdept NOT IN ( 'IS', 'MA', 'CS' ); (4)字符匹配。 谓词LIKE可以用来进行字符串的匹配,常用于模糊查找,它判断列值是否与指定的字 符串格式相匹配。其语法格式如下。 [NOT] LIKE <匹配串> [ESCAPE <换码字符>] 【例3.17】 查询学号为200215121的学生的详细情况。 SELECT * FROM Student WHERE Sno LIKE '200215121'; 等价于: SELECT * FROM Student WHERE Sno = ' 200215121 '; 匹配串也可以是含通配符%和_的字符串。%代表任意长度的字符,如a%b表示以a开 头,以b结尾的任意长度的字符串;_代表单个字符,如a_b表示以a开头,以b结尾的长度 56 数据库原理与应用(微课版) 为3的任意字符串。 【例3.18】 查询所有姓刘学生的姓名、学号和性别。 SELECT Sname, Sno, Ssex FROM Student WHERE Sname LIKE '刘%'; 【例3.19】 查询姓“欧阳”且全名为三个汉字的学生的姓名。 SELECT Sname FROM Student WHERE Sname LIKE '欧阳_'; 使用换码字符将通配符转义为普通字符。 【例3.20】 查询DB_Design课程的课程号和学分,其中的_为普通字符。 SELECT Cno,Ccredit FROM Course WHERE Cname LIKE 'DB_Design' ESCAPE '_ '; 【例3.21】 查询以"DB_"开头,且倒数第3个字符为i的课程信息。 SELECT * FROM Course WHERE Cname LIKE 'DB_%i_ _' ESCAPE ' _ '; (5)多重条件查询。 逻辑运算符AND和OR可用来连接多个查询条件。如果这两个运算符同时出现在同 一个WHERE条件子句中,则AND的优先级高于OR,用户可以用括号改变优先级。 【例3.22】 查询计算机系年龄在20岁以下的所有女生的信息。 SELECT * FROM Student WHERE Sdept='计算机系' and Sage<20 and Ssex='女'; 3.ORDERBY 子句 用户可以用ORDER BY 子句指定按照一个或多个属性列的升序(ASC)或降序 (DESC)排列输出查询结果,默认值为升序。当排序的属性包含空值时,空值在升序中最先 显示,在降序中最后显示。 【例3.23】 查询选修了1号课程的选修信息,按照成绩的降序排列。 SELECT * FROM SC WHERE Cno='1' ORDER BY Grade DESC; 第3章 关系数据库语言———SQL 57 聚集查询 4.聚集函数 SQL提供的常用聚集函数如表3.4所示。 表3.4 常用聚集函数 聚集函数含 义 COUNT([DISTINCT|ALL]*) 统计行数 COUNT([DISTINCT|ALL]<列名>) 统计给定属性列的值的个数 SUM([DISTINCT|ALL]<列名>) 计算给定属性列的总和 AVG([DISTINCT|ALL]<列名>) 计算给定属性列的平均值 MAX([DISTINCT|ALL]<列名>) 求给定属性列的最大值 MIN([DISTINCT|ALL]<列名>) 求给定属性列的最小值 说明:如果指定DISTINCT短语,则表示在计算时取消重复指定属性列中的重复值。 默认情况为ALL值,即不取消对重复值的计算。 【例3.24】 统计数学系的学生总人数。 SELECT COUNT(*) 数学系 FROM Student WHERE Sdept='数学系'; 【例3.25】 计算选修了5号课程学生的平均成绩。 SELECT, AVG(Grade) '5' FROM SC WHERE Sno='5'; 5.GROUPBY 子句 GROUPBY分组子句是对查询结果按给定的属性或属性集进行分组。对查询结果分 组的目的是细化聚集函数的作用对象。如果未对查询结果分组,聚集函数将作用于整个查 询结果;进行分组后,聚集函数将作用于每一个组,即对每个组分别统计。 【例3.26】 查询每门课程的选课人数。 要查询每门课程的选课人数,则需要对表SC按照课程号进行分组,课程号相同的为同 一个组,对每个组分别计算行数。 SELECT Cno, COUNT(*) FROM SC GROUP BY Cno; 查询结果为 Cno COUNT(*) 1 2 2 1 3 2 58 数据库原理与应用(微课版) 【例3.27】 计算每个学生的平均成绩。 要计算每个学生的平均成绩,则需要对SC中按学号进行分组,学号相同的为同一个 组,对每个组计算平均成绩。 SELECT Sno, AVG(Grade) FROM SC GROUP BY Sno; 执行结果为 Sno AVG(Grade) 202005001 75 202008019 92 202006056 85 如果对分组后统计的结果进行筛选,要求只输出满足条件的组,则用HAVING 子句来 给出限定条件。 【例3.28】 输出学生人数小于100人的系及总人数。 SELECT Sdept, COUNT(*) FROM Student GROUP BY Sdept HAVING COUNT(*)<100; 说明: (1)HAVING子句和WHERE子句都是筛选条件,但WHERE作用于基表或视图,而 HAVING子句作用于组。 (2)聚集函数可以出现在SELECT 子句、HAVING 子句和ORDERBY 子句之后,不 能出现在WHERE子句后。 (3)HAVING子句必须紧随GROUPBY子句,不能单独出现。 (4)带GROUPBY子句的SELECT输出只能是分组属性和聚集函数,不能输出与分 组无关的属性列。 空值查询 6.空值查询 (1)空值NULL的含义。 在SQL中允许某些元组在某个属性列上取空值NULL,用来表示“不知道”或“不存在” 或“无意义”的值。如某个学生的年龄取值为NULL,表示不知道该学生的年龄,该学生年 龄的值是存在的,但不知道该值是什么;又如某个学生缺考某门课程,其成绩取值为空值,表 示该学生本课程的成绩不存在,不是其他任何数值。 外连接运算可导致某些元组中产生空值,某些元组的插入也可能产生空值,但存在约束 为NOTNULL(非空值)或UNIQUE(唯一值)的属性值不允许为空值。 (2)空值的运算规则。 空值NULL作为一种特殊的属性值也可以参加运算,但它不是常量,不可以直接将 第3章 关系数据库语言———SQL 59 NULL作为一个操作数,其运算规则如下。 ① 空值NULL 与任何值(包括另一个NULL)进行算术运算,其结果仍然是空值 NULL。 ② 当使用比较运算符(如=或<)时,比较空值NULL与任何值(包括另一个NULL) 时,其结果都为UNKNOWN。值UNKNOWN 是另外一个与TRUE和FLASE相同的布 尔值,传统的二值逻辑运算扩展成了三值逻辑。三值逻辑运算的结果如表3.5所示。 表3.5 三值逻辑真值表 X Y X ANDY X ORY NOTX TRUE TRUE TRUE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE UNKNOWN UNKNOWN TRUE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE TRUE FALSE UNKNOWN FALSE UNKNOWN TRUE UNKNOWN TRUE UNKNOWN TRUE UNKNOWN UNKNOWN FALSE FALSE UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN (3)与空值有关的查询。 空值是一个很特殊的值,含有不确定性,需要做特殊的处理。判断一个属性的值是否为 空值时,必须用ISNULL或ISNOTNULL来表示,不能直接有关系运算符。 【例3.29】 查询缺考学生的学号和课程号。 SELECT Sno,Cno FROM SC WHERE Grade is NULL; 查询涉及允许为空值NULL的属性时,在WHERE条件表达式中需要特别注意有关 NULL的特殊的运算规则。 【例3.30】 查询成绩不合格的学生学号。 SELECT Sno FROM SC WHERE Grade<60 or Grade is NULL; 查询返回WHERE条件为真的结果,而NULL与60比较运算的结果为UNKNOWN, Grade<60的条件只能查找出参加了考试而不及格的学生,不能查询出未参加考试的学生 学号,因此要找出所有不合格的学生需要合并上GradeisNULL的条件。 当NULL出现在集合中时,要注意其逻辑运算规则。 【例3.31】 查询年龄不为空,也不为18岁和19岁的学生信息。 60 数据库原理与应用(微课版) SELECT * FROM Student WHERE Sage not in (18,19, NULL); 无论数据库中的值如何,该查询都将返回空值。WHERE条件等价于“Sage<>18and Sage<>19andSage<>NULL”,Sage<>NULL运算的结果为UNKNOWN,“Sage<> 18andSage<>19andSage<>NULL”的结果只能是FALSE或UNKNOWN,WHERE 条件只在为真时返回查询结果。 3.3.2 多表查询 若一个查询同时涉及两个及以上的表或视图,则称为多表查询或连接查询。连接查询 包括等值连接查询与非等值连接查询、自然连接查询、自身连接查询、外连接查询和复合条 件连接查询等。 1.等值连接与非等值连接查询 连接查询的WHERE子句中用来连接两个表的条件称为连接条件或连接谓词,其一般 格式为 [<表名1>].<列名1><比较运算符>[<表名2>].<列名2> 其中,比较运算符包括=、>、<、<>等。 连接谓词也可以使用下面的形式: [<表名1>].<列名1> BETWEEN [<表名2>].<列名2> AND [<表名2>].<列名2> 若比较运算符为=称为等值连接,其他比较运算符则称为非等值连接。 连接谓词中的列名称为连接字段,连接条件中的各连接字段类型必须是可比的,但名字 不一定相同。若查询涉及多个表时,表中可能会有两个或两个以上的属性具有相同的名字, 需要明确指定这些相同名字的属性如何被使用,SQL通过在属性前加上关系名和一个点运 算来解决,如R.A 表示关系R 的属性A 。 【例3.32】 查询每个学生及其选修课程的情况。 SELECT Student.*, SC.* FROM Student, SC WHERE Student.Sno=SC.Sno; 该查询的输出涉及学生表Student和选修表SC中的信息,因此要将这两个表通过共同 的属性(学号Sno)进行连接;属性列“学号”既出现在学生表Student中也出现在选修表SC 中,因此要明确指明使用学生表中的学号还是选修表中的学号。 【例3.33】 查询每个学生的学号、姓名、选修的课程名称及成绩。 SELECT Student.Sno, Sname, Cname, Grade FROM Student, SC, Course WHERE Student.Sno=SC.Sno AND SC.Cno=Course.Cno; 第3章 关系数据库语言———SQL 61 该查询的输出涉及学生表Student、选修表SC和课程表Course中的信息,是多表连接 的查询,要考虑多个表之间的连接关系。例如,学生表Student与选修表SC通过共同的属 性“学号Sno”进行连接,选修表SC与课程表Course通过共同的属性“课程号Cno”进行连 接,多表连接时所有的连接条件必须同时成立,用逻辑与AND表示。 2.自然连接 自然连接是一种特殊的等值连接,是在等值连接中将重复的属性列去掉。 【例3.34】 查询学生选修课程的情况,要求输出学生的姓名、学号、课程号和成绩。 SELECT Sname, Student.Sno, Cno, Grade FROM Student,SC WHERE Student.Sno=SC.Sno; 3.自连接 SELECT查询语句不但支持不同表之间的连接,而且支持任意表自身的连接。一个表 与其自身进行连接称为表的自连接。当进行自连接查询时,需要在FROM 子句中将关系R 列出多次,对每一个R 的出现定义一个别名来进行区分。在SELECT和WHERE子句中, 通过别名加点符号来消除关系R 的属性歧义。别名可以作为关系R 的另外一个名字出现 在需要的地方。 【例3.35】 查询先修课相同的课程号。 SELECT C1.Cno, C2.Cno FROM Course C1, Course C2 WHERE C1.Cpno=C2.Cpno and C1.Cno<>C2.Cno; 在FROM 子句中为表Course声明了两个别名C1和C2,在SELECT 子句中输出表 Course两行元组的Cno字段,在WHERE子句中由别名C1和C2引用来表示表Course二 行元组的Cpno字段值相同;为了避免二行元组是相同的,在WHERE子句中加上了第二个 条件。 4.外连接 在通常的连接操作中,只有满足连接条件的元组才能作为结果输出。但假如想查询学 生表中每个学生的基本情况及选课情况,若某个学生没有选课,则只输出该学生的基本情 况,选课信息为空,这样的查询要求就需要使用外连接。外连接是通过在悬浮元组里填充空 值来使之成为查询结果。 外连接的基本格式为 SELECT [ALL|DISTINCT] <目标列表达式> [, <目标列表达式>] … FROM <表1> LEFT|RIGHT|FULL [OUTER] JOIN <表1> ON <连接条件> [ WHERE <条件表达式> ] 外连接包括左外连接(LEFT)、右外连接(RIGHT)和全外连接(FULL)三种。左外连 接列出左边关系中所有的元组,右边关系悬浮元组里填充空值;右外连接列出右边关系中所 有的元组,左边关系悬浮元组里填充空值;全外连接则输出两个表的所有元组,左、右两边关 系中悬浮元组里填充空值。 62 数据库原理与应用(微课版) 【例3.36】 查询所有学生的选课情况,包括未选课的学生。 SELECT Student.Sno, Sname, Ssex, Sdept, Cno, Grade FROM Student LEFT JOIN SC ON (Student.Sno=SC.Sno); 针对图3.2的数据库表其查询结果为 学号 Sno 学生姓名 Sname 性别 Ssex 年龄 Sage 所在系 Sdept 课程号 Cno 成绩 Grade 202005001 李洋男18 计算机系1 80 202005001 李洋男18 计算机系3 80 202008019 王欣女18 数学系2 92 202006056 张楠女18 计算机系1 70 202006056 张楠女18 计算机系3 90 202006758 谢小平男19 电子系NULL NULL 5.复合条件连接 一条SQL语句可以同时完成选择和连接查询,这时WHERE子句由连接谓词和选择 谓词组成复合条件。 【例3.37】 查询选修了2号课程且成绩在90分以上的学生的学号、姓名和成绩。 SELECT Student.Sno, Sname, Grade FROM Student, SC Where Student.Sno=SC.Sno and Cno='2' and Grade>90; 选择谓词Cno=2' '和Grade>90从选修表SC中找出满足条件的元组形成中间结果,再 通过连接谓词Student.Sno=SC.Sno将学生表Student和中间结果连接起来输出满足条件 的结果。 3.3.3 嵌套子查询 在SQL中,一个查询可以通过不同的方式被用来计算另一个查询。当某个查询是另一 个查询的一部分时,称之为子查询。集合查询的并、交、差就是通过子查询来完成的。 子查询可能返回单个常量,这个常量能在WHERE子句中与另一个常量进行比较;子 查询也可能返回关系,该关系可以在WHERE中使用,也可以出现在FROM 子句中。子查 询以层层嵌套的方式来构造程序正是SQL中结构化的含义所在。 需要特别指出的是,子查询语句中不允许使用ORDERBY 子句,ORDERBY 子句只 能对最终查询结果排序。 WHERE子句 嵌套子查询 1.WHERE子句嵌套子查询 (1)IN 子查询。 带有IN 谓词的子查询是指父查询与子查询之间用IN 进行连接,用于判断父查询的某 个属性的值是否在子查询的结果中。IN 表示某元素属于某个集合,NOTIN 则表示某元素 第3章 关系数据库语言———SQL 63 不属于某个集合。谓词IN 是嵌套查询中最经常使用的谓词。 【例3.38】 查询选修了3号课程的学生学号和姓名。 SELECT Sno, Sname FROM Student WHERE Sno IN (SELECT Sno FROM SC WHERE Cno='3'); 本例中,子查询的查询条件不依赖于父查询,称为不相关子查询。不相关子查询是最简 单的一类子查询。 此查询也可以用连接查询完成: SELECT Student.Sno, Sname FROM Student, SC WHERE Student.Sno=SC.Sno and Cno='3'; 可见实现同一个查询可以用很多种方法,不同的方法其执行效率可能会存在差别。 查询涉及多个关系时,用嵌套查询逐步求解,层次清楚,易于构造,具有结构化程序设计 的特点。 【例3.39】 查询选修了课程名称为“操作系统”的学生学号和姓名。 SELECT Sno, Sname FROM Student WHERE Sno IN (SELECT Sno FROM SC WHERE Cno IN (SELECT Cno FROM Course WHERE Cname='操作系统')); 本查询也可以用连接查询替代: SELECT Student.Sno, Sname FROM Student, SC,Course WHERE Student.Sno=SC.Sno and SC.Cno=Course.Cno and Cname='操作系统'; 但有些嵌套子查询不能由连接查询替代,如例3.40。 【例3.40】 查询没有选修3号课程的学生学号和姓名。 SELECT Sno, Sname FROM Student WHERE Sno NOT IN (SELECT Sno FROM SC WHERE Cno='3'); (2)带比较运算符的子查询。 带有比较运算符的子查询是指父查询与子查询之间用比较运算符进行连接。当用户能 64 数据库原理与应用(微课版) 确切知道子查询结果返回的是单个值时,可以用=、>、<、<>等比较运算符。 【例3.41】 查询与“刘霞”在同一个系的学生的学号和姓名。 SELECT Sno,Sname FROM Student WHERE Sdept=(SELECT Sdept FROM Student WHERE Sname='刘霞'); 【例3.42】 查询“计算机系”超出全校学生平均年龄的学生学号和姓名。 SELECT Sno,Sname FROM Student WHERE Sdept='计算机系' and Sage > (SELECT AVG(Sage) FROM Student); (3)带ANY或ALL谓词的子查询。 在带比较运算符的子查询中,当子查询返回多值时,要与ANY 或ALL谓词修饰符配 合使用。 ANY:表示任意一个值,在进行运算时,只要子查询中有一行能使结果为无休止,则结 果就为真。 ALL:表示所有值,在进行比较运算时,子查询中的所有行都使结果为真时,结果才为 真。其具体语义如下。 >ANY 大于子查询结果中的某个值 >ALL 大于子查询结果中的所有值 =ANY 大于或等于子查询结果中的某个值 >=ALL 大于或等于子查询结果中的所有值 <=ANY 小于或等于子查询结果中的某个值 <=ALL 小于或等于子查询结果中的所有值 =ANY 等于子查询结果中的某个值 <>ANY 不等于子查询结果中的某个值 <>ALL 不等于子查询结果中的任何一个值 【例3.43】 查询数学系比计算机系任意一个学生年龄都大的学生姓名和年龄。 SELECT Sname, Sage FROM Student WHERE Sdept='数学系' and Sage>ALL (SELECT Sage FROM Student WHERE Sdept='计算机系' ); 事实上,用聚集函数实现子查询通常比直接用ANY 或ALL 查询效率要高。ANY、 ALL与聚集函数的对应有关系如表3.6所示。 第3章 关系数据库语言———SQL 65 表3.6 ANY、ALL谓词与聚集函数的等价转换关系 = <> < <= > >= ANY IN -- MIN >=MIN ALL -- NOTIN MAX >=MAX (4)带EXIST谓词的子查询。 EXIST代表存在量词.。带有EXIST 谓词的子查询不返回任何实际数据,只产生逻 辑真值“TRUE”或逻辑假值“FALSE”。因此,带EXIST谓词的子查询中,其目标列表达式 通常用“*”,因为带EXIST的子查询只返回“TRUE”或“FALSE”,给出列名无实际意义。 使用EXIST,若子查询结果为非空,则外层WHERE子句返回真值,否则返回假值。 【例3.44】 查询选修了3号课程的学号和姓名。 SELECT Sno, Sname FROM Student WHERE EXISTS (SELECT * FROM SC, Student WHERE SC.Sno=Student.Sno and Cno='3' ); 该子查询与前面IN 子查询不同,其子查询的条件依赖于父查询的某个属性值,这类查 询称为相关子查询。相关子查询的处理过程是先取父查询表中的第1行元组到子查询,若 子查询结果为非空,则WHERE返回为真,则取父查询的当前元组放入结果表;再取父查询 表的下一行元组,重复此过程直到父查询表全部访问完。 与EXIST谓词相对应的是NOTEXIST。使用NOTEXIST,若子查询结果为空,则外 层WHERE子句返回真值,否则返回假值。SQL没有提供表示全称量词.的谓词。 【例3.45】 查询选修了全部课程的学生姓名。 在Student表中查找学生,要求这个学生选修了全部课程。换句话说,即在S 表中查找这 样的学生:在C 表中不存在一门课程这个学生没有选。按照此语义,可写出SQL查询语句为 SELECT Sname FROM Student WHERE NOT EXISTS (SELECT * FROM C WHERE NOT EXISTS (SELECT * FROM SC WHERE SC.Sno=Student.Sno and SC.Cno=C.Cno)); 【例3.46】 查询所学课程包括学号3的学生所学课程的学生学号。 在SC表中查找一个学生,对于3号学生所学的每一门课程,该学生都学了。将其改为 双重否定形式是:不存在3号学生所选修的课程不被某个学生选修,这个学生就是要查找 的学生。 SELECT DISTINCT Sno FROM SC X 66 数据库原理与应用(微课版) WHERE NOT EXISTS (SELECT * FROM SC Y WHERE Sno='3' and NOT EXISTS (SELECT * FROM SC Z WHERE Y.Cno=Z.Cno AND X.Sno=Z.Sno)); FROM 子句 嵌套子查询 2.FROM 子句嵌套子查询 子查询的另一个作用是在FROM 子句中当关系使用。在FROM 列表中,除了可以使 用存储关系以外,还可以使用括起来的子查询。由于这个子查询的结果没有名字,必须给它 取一个别名,然后就可以像引用FROM 子句中的关系一样引用子查询。 【例3.47】 查询输出计算机系平均成绩在85分以上的学生学号、姓名和平均成绩。 SELECT Student.Sno, Sname, AVGGD FROM Student, (SELECT Sno, AVG(Grade) AVGGD FROM SC GROUP BY Sno HAVING AVG(Grade) >85) TEMP WHERE Student.Sno=TEMP.Sno AND Sdept='计算机系'; 第一个FROM 子句嵌套了一个子查询,该子查询找出了平均成绩在85分以上的学生 学号和平均成绩,用别名TEMP表示。WHERE子句则对学生表Student进行筛选后与子 查询进行连接。 集合查询 3.3.4 集合查询 SELECT语句的查询结果是元组的集合,所以多个SELECT语句的结果可以进行集合 操作。集合操作主要包括并操作UNION、交操作INTERSECT和差操作EXCEPT。参加 集合操作各个SELECT语句的查询结果的列数必须相同,对应的数据类型也必须相同。 【例3.48】 查询计算机系和数学系的学生信息。 SELECT * FROM Student WHERE Sdept='计算机系' UNION SELECT * FROM Student WHERE Sdept='数学系'; 该查询也可用逻辑运算OR来实现,其SQL语句为 SELECT * FROM Student WHERE Sdept='计算机系' OR Sdept='数学系'; 【例3.49】 查询计算机系年龄小于20岁的学生信息。 SELECT * FROM Student WHERE Sdept='计算机系'