1
数据库原理与应用技术
1.6.3.2 5.3.2 单表查询
5.3.2 单表查询

所谓单表查询是指仅针对一个表的查询。

1. 选择表中的列(字段)

大多数情况下,用户只对表中的部分属性列感兴趣,可以通过SELECT子句的<目标列表达式>中指定的列来实现,对应关系代数中的投影运算。

【例5-8】分别选择students数据库中student、course、enroll表的所有列进行查询,并写出相关语句。

解:①SELECT * FROM student

②SELECT * FROM course

③SELECT * FROM enroll

语句中的“*”号表示显示该表中的所有字段。

查询结果如图5-1至图5-3所示。

图5-1 例5-8图1

图5-2 例5-8图2

图5-3 例5-8图3

【例5-9】选择student表中的sno、sname、department列进行查询,并写出相关语句。

解:SELECT sno,sname,department FROM student

查询结果如图5-4所示。

图5-4 例5-9图

SELECT语句后面直接跟需要显示的字段,题目要求选择student表中的sno、sname、department这几个字段,所以sno、sname、department紧跟SELECT之后,并且字段之间用逗号隔开。

【例5-10】查询student表中所有系的名字,去掉重复行,写出相关语句。

解:SELECT DISTINCT department FROM student

结果如图5-5所示。

DISTINCT在此表示进行列选择之后去掉重复行,若无DISTINCT,则例5-10显示结果如图5-6所示。

图5-5 例5-10图1

图5-6 例5-10图2

SELECT department FROM student语句等价于SELECT all department FROM student。

【例5-11】查询全体学生的姓名、性别及其出生年份,写出相关语句。

解:SELECT sname,sex,2013-age FROM student

结果如图5-7所示。

从图5-7中可以看出,最后一列显示无列名,可以用AS重命名这一列,AS可以省略不写,其语句为

SELECT sname,sex,2013-age AS 出生年份 FROM student

AS除了可以对属性进行重命名外,也可以对表进行重命名,方法同属性重命名。结果如图5-8所示。

图5-7 例5-11图1

图5-8 例5-11图2

除了上面使用AS表示出生年份外,也可以直接使用“出生年份”表示,其语句为

SELECT sname,sex, '出生年份',2013-age FROM student

结果如图5-9所示。

图5-9 例5-11图3

2. 选择表中的行(记录)

查询满足指定条件的元组可以通过WHERE子句实现,它对应关系代数中的选择运算。WHERE子句允许用户确定一个谓词。带有WHERE子句的SELECT语句,执行结果只给出使谓词为真的那些记录值。WHERE之后的谓词就是查询条件。WHERE子句常用的查询条件如表5-2所示。

表5-2 常用的查询条件

说明:集合成员运算符用于检查一个属性值是否属于集合中的值。确定范围运算符中的BETWEEN后是下限,AND后是上限。字符串匹配运算符用于构造条件表达式中的字符匹配, LIKE前的列名必须是字符串类型。算术运算符用于字符串比较时,字符串从左向右进行。逻辑运算符用于构造复合表达式。

1)算术运算符和逻辑运算符

【例5-12】现有student表,查询所有计算机系的学生,写出相关语句。

解:SELECT * FROM student WHERE department='计算机'

结果如图5-10所示。

图5-10 例5-12图1

例5-12显示了所有满足条件的行记录,列出了所有计算机系的学生,逻辑运算符可以和算术运算符同时使用。例如,查找山东籍的计算机系学生,查询语句如下:

SELECT * FROM student WHERE department='计算机' AND bplace='山东'

结果如图5-11所示。

图5-11 例5-12图2

【例5-13】查询年龄在22岁以上学生的姓名、性别和年龄,写出相关语句。

解:SELECT sname,sex,age FROM student WHERE age>22

结果如图5-12所示。

上述语句也可以写为SELECT sname,sex,age FROM student WHERE NOT age<=22。

2)范围条件用BETWEEN…AND…表示

BETWEEN…AND…用于判断一个表达式的值是否落在某一个指定的范围内,选取落在范围内的数据行。

格式为:<列名> [NOT] BETWEEN <下限> AND <上限>

此格式中的<下限>小于<上限>。当由<列名>所指定的列的当前值在(或不在,用NOT)所指定的下限和上限之间(包括两个端点的值在内)时,则此表达式为真;否则为假。该表达式与下面的逻辑表达式等效:

不选NOT ,<列名>>=<下限> AND <=<上限>

选NOT,<列名><<下限> OR ><上限>

【例5-14】在student表中,查询年龄在20~22岁之间的学生的姓名和年龄,写出相关语句。

解:SELECT sname,age FROM student WHERE age BETWEEN 20 AND 22

结果如图5-13所示。

图5-12 例5-13图

图5-13 例5-14图

上述语句中的BETWEEN…AND可以用算术运算符和逻辑运算符替换为

SELECT sname,age FROM student WHERE age>=20 AND age<=22

若查询年龄不在20~22岁之间的学生的姓名和年龄,查询语句为

SELECT sname,age FROM student WHERE age NOT BETWEEN 20 AND 22

请读者思考该语句如何用算术运算符和逻辑运算符替换。

3)组属条件用IN表示

IN用于判断表达式的值是否落在指定的组内,选取属于这一组内的数据行。

格式如下:

<列名> [NOT] IN {(<常量表>)|(<子查询>)}

<常量表>表示用逗号分开的若干个常量。当<列名>所指定列的当前值包含在由<常量表>所给定的值之内时,则此判断式为真;否则为假。若在IN关键字后面不是使用<常量表>,而是使用<子查询>,则当由<列名>所指定列的当前值包含在子查询结果之中时,则其判断式为真;否则为假。若在此判断式中选用NOT关键字,则判断结果正好相反。

【例5-15】在student表中,查询家在湖南和山东的学生的学号、姓名、性别和籍贯,写出相关语句。

解:SELECT sno,sname,sex,bplace FROM student WHERE bplace IN('湖南','山东')

结果如图5-14所示。

上述语句中的IN可以用算术运算符和逻辑运算符替换为

SELECT sno,sname,sex,bplace FROM student WHERE bplace='湖南' or bplace='山东'

若查询籍贯不在湖南和山东的学生,则查询语句如下:

SELECT sno,sname,sex,bplace FROM student WHERE bplace NOT IN('湖南', '山东')

请读者思考该语句如何用算术运算符和逻辑运算符替换。

【例5-16】在student表和enroll表中,查询成绩在80分以上的学生的学号和姓名,写出相关语句。

解:SELECT sno,sname FROM student WHERE

sno IN(SELECT sno FROM enroll WHERE grade>80)

结果如图5-15所示。

图5-14 例5-15图

图5-15 例5-16图

若例5-16改为查询成绩在80分以下的学生的学号和姓名,则查询语句为

SELECT sno,sname FROM student WHERE

sno NOT IN(SELECT sno FROM enrolls WHERE grade>80)

4)模式匹配条件用LIKE表示

LIKE用于判断一个包含字符串的数据列的值是否匹配某一指定的模式,选取与模式相匹配的数据行。格式如下:

<字符串列名> NOT LIKE <字符表达式>

当<字符串列名>的当前值与<字符表达式>的值相匹配时,此判断式为真,否则为假。当选用NOT关键字时,判断结果相反。通常来说,<字符表达式>为字符常量,若在其中使用下画线(_),则表示能和任何一个字符匹配。若使用百分号(%),则表示能和任意多个(含零个)字符匹配。

【例5-17】在student表中,查询所有姓“张”的学生,写出相关语句。

解:SELECT * FROM student WHERE sname LIKE '张%'

结果如图5-16所示。

图5-16 例5-17图

【例5-18】查询名字中第二个字为“春”字的学生的姓名和性别,写出相关语句。

解:SELECT sname,sex FROM student WHERE sname LIKE '_春%'

结果如图5-17所示。

【例5-19】查询姓名中有“春”字的学生的姓名和性别,写出相关语句。

解:SELECT sname,sex FROM student WHERE sname LIKE '%春%'

结果如图5-18所示。

如果要查询的字符串本身就含有“%”或“_”,这时就要用ESCAPE换码字符对通配符进行转义。

【例5-20】查询课程名以“操_”开头且倒数第二个汉字是“系”的课程情况,写出相关语句。

解:SELECT * FROM course WHERE cname like '操\_%系_'ESCAPE'\'

结果如图5-19所示。

图5-17 例5-18图

图5-18 例5-19图

图5-19 例5-20图

因为表中无“操_”开头的课程,所以结果为空。

ESCAPE'\'中的“\”表示为换码字符,匹配串'操\_%系_'中的第一个“_”前有换码字符\,故它被转义为普通字符下画线“_”,而“%”及“系”字后的“_”均无换码字符“\”,故它们仍为通配符。换码字符是可变化的,一般取不常用的符号。例5-20中,若匹配符中本身含“\”,则换码字符可取“?”。

【例5-21】查询王丽同学的详细情况,写出相关语句。

解:SELECT * FROM student WHERE sname LIKE '王丽'

该语句等价于

SELECT * FROM student WHERE sname='王丽'

结果如图5-20所示。

图5-20 例5-21图

如果LIKE后面的匹配符中不含通配符,则可以用等于(=)运算符取代LIKE谓词,用不等于(!=或<>)取代NOT LIKE谓词。若LIKE后面的匹配符含通配符,则不可以用“=”取代。

5)涉及空值的查询

【例5-22】某些学生选课后没有参加考试,所以他只有选课记录,没有考试成绩。查询没有考试成绩的学生的详细情况,写出相关语句。

解:SELECT * FROM enroll WHERE grade IS NULL

结果如图5-21所示。

图5-21 例5-22图

注意这里的“IS”不能用“=”代替。

同理,查询所有有成绩的学生信息的语句为

SELECT * FROM enroll WHERE grade IS NOT NULL

3. 查询中集函数的使用

为了增强检索功能,SQL提供了表5-3所示的常用集函数及其功能介绍。

表5-3 常用集函数及其功能

其中,函数SUM和AVG所涉及的属性必须是数值型的,特殊函数COUNT(*)用于统计元组数。

如果指定 DISTINCT 选项,则表示在计算时要取消指定列中的重复值。如果不指定DISTINCT选项或指定ALL选项(ALL为默认值),则表示不取消重复值。聚集函数计算时一般均忽略空值,即不统计空值。

【例5-23】查询在student表中学生的总人数,写出相关语句。

解:SELECT COUNT(*) AS 总人数 FROM student

结果如图5-22所示。

【例5-24】计算student表中学生的平均年龄,写出相关语句。

解:SELECT AVG(age) as 平均年龄 FROM student

结果如图5-23所示。

【例5-25】查询选修了课程的学生人数,写出相关语句。

解:SELECT COUNT(DISTINCT sno) FROM enroll

结果如图5-24所示。

图5-22 例5-23图

图5-23 例5-24图

图5-24 例5-25图

学生每选修一门课程,在enroll表中都有一条相应的记录。一个学生要选修多门课程,为避免重复计算学生人数,必须在COUNT函数中使用DISTINCT选项。

【例5-26】查询选修c1号课程的学生最高分数,写出相关语句。

解:SELECT MAX(grade) 最高分 FROM enroll WHERE cno= 'c1'

结果如图5-25所示。

4. 查询结果的分组

GROUP BY子句将查询结果表按指定列的值进行分组,值相等的分为一组。分组的目的是将集函数的作用对象细化,且分组后集函数将作用在每个组上,也就是说,每个组都有一个函数值。把FROM子句中的关系按分组属性分为若干组,同一组内所有的元组在分组属性上具有相同值。分组属性可以是单个属性,也可以是多个属性的组合。

【例5-27】求各个课程号及相应的选课人数,写出相关语句。

解:SELECT cno,COUNT(sno) 选课人数 FROM enroll GROUP BY cno

结果如图5-26所示。

该语句按cno的值对查询结果进行分组,所有具有相同cno值的元组分为一组,然后对每一组作用集函数COUNT进行计算,以求得每门课的选课人数。

【例5-28】查询student表中每个系在3个以上的学生的所在系,写出相关语句。

解:SELECT department FROM student GROUP BY department HAVING COUNT(*)>=3

结果如图5-27所示。

图5-25 例5-26图

图5-26 例5-27图

图5-27 例5-28图

本例中用GROUP BY子句将department进行分组,再用集函数COUNT对每一组进行计数。HAVING子句用于指定选择组的条件。执行此语句后,只有满足条件(department相同的记录有3条以上)的组才会被选出来。

注意WHERE与HAVING的区别,在这两个子句后都跟条件,但它们的作用对象不同。WHERE子句作用于基本表或视图,从中选择满足条件的记录;HAVING作用于分组,从中选择满足条件的组,所以该子句只能同GROUP BY子句配合使用,筛选符合条件的分组信息。

5. 查询结果的排序

ORDER BY子句按其后所跟的列名对查询结果进行排序。查询结果将首先按<列名1>的值进行排序,若该列的值相同,则再按<列名2>的值进行排序,依此类推。若其后带ASC,则表示按值的升序排列查询结果;若其自带DESC,则表示按值的降序排列查询结果;若不指定排序方式,则默认按升序排列查询结果。

【例5-29】在student表中按年龄的升序查询出所有学生的记录,写出相关语句。

解:SELECT * FROM student ORDER BY age ASC

结果如图5-28所示。

图5-28 例5-29图

【例5-30】查询选修了c3号课程的学生的信息,查询结果按分数降序进行排列,写出相关语句。

解:SELECT sno,grade FROM enroll WHERE cno='c3' ORDER BY grade DESC

结果如图5-29所示。

图5-29 例5-30图

【例5-31】查询全体学生情况,查询结果按所在系的系号升序进行排列,同一系中的学生按年龄降序进行排列,写出相关语句。

解:SELECT * FROM student ORDER BY department,age DESC

结果如图5-30所示。

图5-30 例5-31图