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图