6.4.4 查询
数据查询是数据库的核心操作。
SQL查询语句的一般格式为:
SELECT[ALL|DISTINCT]<目标列表达式>[,<目标列表达式>]...
FROM<表名或视图名>[,<表名或视图名>]...
[WHERE<条件表达式>][GROUP BY<列名1>[HAVING<条件表达式>]]
[ORDER BY<列名2>][ASC|DESC]];
说明:根据WHERE子句的条件表达式,从FROM子句指定的基本表或视图中找出满足条件的元组,再按SELECT子句中的目标列表达式,选出元组中的属性值形成结果表。如果有GROUP子句,则将结果按<列名1>的值进行分组,该属性列值相等的元组为一个组,每个组产生结果表中的一条记录。如果GROUP子句带HAVING短语,则只有满足指定条件的组才予以输出。如果有ORDER子句,则结果表还要按<列名2>的值的升序或降序排列。
有时为了阅读方便,可以用关键字AS给计算机屏幕上显示出来的结果关系中的属性另外取一个名称。格式为:
SELECT属性的原名AS别名
[例11]以Student表为例,在该表中查询全体学生的学号和姓名,并在查询结果中以“学号”和“姓名”为列名进行显示。
SELECT Sno as学号,Sname as姓名
FROM Student;
1)单表查询
(1)选择表中的若干列
选择表中全部列或部分列的运算又称为“投影”。其变化方式主要表现在SELECT子句的<目标列表达式>。
①查询指定列
[例12]查询全体学生的学号、姓名和系名。
SELECT Sno,Sname,Sdept,
FROM Student;
②查询全部列
有两种方法,一种是在SELECT关键字后面列出所有列名;另一种是将<目标表达式>指定为*,此时查询结果的目标列的显示顺序与其在基本表中的顺序相同。[例13]显示全体学生的详细信息。可以用以下两种方法表示:
a.SELECT Sno,Sname,Ssex,Sage,Sdept
FROM Student;
b.SELECT*
FROM Student;
③查询经过计算的值
SELECT子句的<目标列表达式>也可以不是属性列,而是一些计算表达式,即将查询出来的属性列经过一定的计算后列出结果。
[例14]查询全体学生的学号、姓名和出生年份。
SELECT Sno,Sname,2008-sage
FROM Student;
(2)选择表中的若干元组
①消除取值重复的行
投影结果可能会包含许多重复的行,如果要去掉结果表中重复的行,则必须指定DISTINCT短语。
如,查询全体学生所在的系有哪些:
SELECT DISTINCT Sdept
FROM Student;
②查询满足条件的元组通过WHERE子句实现。WHERE子句常用的查询条件有:比较大小、确定范围、确定集合、字符匹配、空值、多重条件。
a.比较大小
用于比较大小的运算符包括:=(等于)、>(大于)、<(小于)、=(等于)、<=(小于等于)、>=(大于等于)、!=或<>(不等于)、!>(不大于)、!<(不小于)。
[例15]查询年龄大于20岁的学生的详细信息。
SELECT*
FROM student
WHERE sage>20;
b.确定范围
要查找属性值在确定范围内的元组,可以用BETWEEN...AND...表示;要查找属性值不在确定范围内的元组,可以用NOT BETWEEN...AND...表示。
[例16]查找年龄在18到20岁之间(即年龄大于等于18、小于等于20)的学生的学号、姓名和年龄。
SELECT Sno,Sname,Sage
FROM Student
WHERE Sage BETWEEN 18AND 20;
又如,查找年龄不在18到20岁之间的学生学号、姓名和年龄:
SELECT Sno,Sname,Sage
FROM Student
WHERE Sage NOT BETWEEN 18AND 20;
c.确定集合
用谓词IN来查找属性值属于指定集合的元组。
[例17]查询信息系、数学系和计算机科学系学生的姓名和性别。
SELECT Sname,Ssex
FROM Student
WHERE Sdept IN(′IS′,′MA′,′CS′);
d.字符匹配
字符匹配可以用来实现模糊查询。在SQL语句中用LIKE来进行字符串的匹配。
语句格式:
[NOT]LIKE′<匹配串>′
说明:<匹配串>可以含有通配符“%”和“-”。其中,“%”代表任意长度的字符串,“-”代表任意单个字符。注意:一个汉字占2个字符的位置。
如果LIKE后面的匹配串中不含通配符,则可以用“=”运算符取代LIKE谓词,用“!=”或“<>”取代NOT LIKE谓词。
[例18]查询姓李(即姓名以“李”字开头,后面为任意字符)的学生的姓名、学号和性别。
SELECT Sname,Sno,Ssex
FROM Student
WHERE Sname LIKE′李%′;
e.涉及空值的查询
可用谓词IS NULL和IS NOT NULL来查询空值和非空值。
注意:这里的′IS′不能用′=′代替。
[例19]查找没有系名的学生的学号和姓名。
SELECT Sno,Sname
FROM Student
WHERE Sdept IS NULL;
f.多重条件查询
用逻辑运算符AND、OR可以联结多个查询条件,AND的优行级高于OR。
[例20]查询年龄在20岁以下的男生的学号和姓名。
SELECT Sno,Sname
FROM Student
WHERE Ssex=′男′AND Sage<20;
(3)对查询结果排序
用ORDER子句指定查询结果的排列顺序。ASC为升序,DESC为降序。
[例21]查询全校女生的学号、姓名和年龄,并将查询结果按年龄降序排列。
SELECT Sno,Sname,Sage
FROM Student
WHERE Ssex=′女′
ORDER BY Sage DESC;
(4)使用集函数
集函数包括:
COUNT([DISTINCT|ALL]*) //统计元组个数
COUNT([DISTINCT|ALL]<列名>) //统计一列中值的个数
SUM([DISTINCT|ALL]<列名>) //计算一列值的总和
AVG([DISTINCT|ALL]<列名>) //计算一列值的平均值
MAX([DISTINCT|ALL]<列名>) //求一列值中的最大值
MIN([DISTINCT|ALL]<列名>) //求一列值中的最小值
[例22]查询学生总人数。
SELECT COUNT(*)
FROM Student;
[例23]查询学校中系的数量。
SELECT COUNT(DISTINCT Sdept)
FROM Student;
(5)对查询结果分组
使用GROUP BY子句可以将查询结果表的各行按一列或多列取值相等的原则进行分组。对查询结果分组的目的是为了细化集函数的作用对象。如果未对查询结果分组,集函数将作用于整个查询结果,即整个查询结果只有一个函数值。如果对查询结果分组,则每一组都将有一个函数值。
[例24]查询各个系的学生人数。
SELECT Sdept,COUNT(Sno)
FROM Student
GROUP BY Sdept;
如果要求按一定的条件对这些结果进行筛选,最终只输出满足指定条件的组,则可以使用HAVING短语指定筛选条件。
[例25]查询学生人数超过300的系名。
SELECT Sdept
FROM Student
GROUP BY Sdept
HAVING COUNT(Sno)>300;
WHERE子句与HAVING短语的根本区别在于:WHERE子句作用于基本表或视图,从中选择满足条件的元组;HAVING短语作用于组,从中选择满足条件的组。
2)连接查询
如果一个查询要对多个表进行操作,则称为连接查询。
连接查询实际上是通过各个表之间共同列的关联性来查询数据的。数据表之间的联系是通过表的字段值来体现的,这种字段称为连接字段。连接操作的目的就是通过加在连接字段的条件将多个表连接起来,以便从多个表中查询数据。
连接查询主要包括等值连接查询、非等值连接查询、自身连接查询、外连接查询和复合条件连接查询。
(1)等值与非等值连接查询
一般格式:
[<表名1>.]<列名1><比较运算符>[<表名2>.]<列名2>
比较运算符主要有:=、>、<、>=、<=、!=。
其他连接谓词形式:
[<表名1>.]<列名1>BETWEEN[<表名2>.]<列名2>AND[<表名2>.]<列名3>
当连接运算符为“=”时,称为等值连接,使用其他运算符时称为非等值连接。
连接谓词中的列名为连接字段。连接条件中的各连接字段类型必须是可比的,但不必是相同的。
为了举例说明连接查询表示方法,下面再引入一张数据表SC(Sno,Cno,Grade),即学生的选课表,由学号、课程号、成绩三个属性列构成。
表SC
[例26]查询每个学生及其选修课程的情况。
SELECT Student.*,SC.*
FROM Student,SC
WHERE Student.Sno=SC.Sno;
查询结果如下:
两种特殊的连接运算:
①卡氏积连接
卡氏积连接不带连接谓词,又称无条件连接。两个表的卡氏积即两表中元组的交叉乘积,即一个表中的每一个元组都要与另一个表中的每一个元组做拼接。卡氏积连接的结果通常会产生一些没有意义的元组,因此很少用。
如:SELECT Student.*,SC.*
FROM Student,SC;
②自然连接
按照两个表中的相同属性进行等值连接,且目标列中去掉了重复的属性列,保留了所有不重复的属性列,则称为自然连接。
[例27]查询每个学生及其选课情况。
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM Student,SC
WHERE Student.Sno=SC.Sno;
则查询结果如下:
(2)自身连接
一个表与其自己进行连接称为自身连接。
[例28]查询与王婷在同一个系的学生的学号和姓名。
SELECT second.Sno,second.Sname
FROM Student first,Student second
WHERE first.Sdept=second.Sdept and first.Sname=′王婷′;
(3)外连接
在通常的连接操作中,只有满足连接条件的元组才能作为结果输出,但有时也想将不满足连接条件的元组输出,这时就要用外连接,其运算符通常为“*”。
外连接分为左外连接、右外连接和全外连接。
左外连接:用来显示符合条件的数据行以及左边表中不符合条件的数据行,此时右边数据行以NULL显示。
右外连接:用来显示符合条件的数据行以及右边表中不符合条件的数据行,此时左边数据行以NULL显示。
全外连接:用来显示符合条件的数据行及左边表、右边表中不符合条件的数据行,此时缺少数据的数据行以NULL显示。
[例29]查询每个学生及其选课情况,如果该生没有选课,则显示其基本信息,选课信息为空。
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM Student left outer join SC on Student.Sno=SC.Sno;
(4)复合条件连接
WHERE子句中有多个条件的连接操作称为复合条件连接。
[例30]查询选修2号课程且成绩在90分以上的所有学生的学号和姓名。
SELECT Student.Sno,Sname
FROM Student,SC
WHERE Student.Sno=SC.Sno AND
SC.Cno=′2′AND
SC.Grade>90;
3)嵌套查询
查询块:一个SELECT-FROM-WHERE语句称为一个查询块。
嵌套查询:将一个查询块嵌套在另一个查询块的WHERE子句或HAVING短语条件中的查询称为嵌套查询或子查询。
嵌套查询的求解方法是由里向外处理。即每个子查询在处理其上一级查询之前求解,子查询的结果用于建立其父查询的查找条件。
注意:子查询的SELECT语句不能使用ORDER BY子句,因为ORDER BY子句只能对最终查询结果排序。
(1)带有IN谓词的子查询
带有IN谓词的子查询是指父查询与子查询之间用IN进行连接,以判断某个属性列值是否在子查询的结果中。谓词IN是嵌套查询中最常使用的谓词。
[例31]查询与王婷在同一个系的学生的学号、姓名和系名。
(2)带有比较运算符的子查询
带有比较运算符的子查询是指父查询与子查询之间用比较运算符进行连接。注意:子查询一定要跟在比较运算符之后。
[例32]查询与07081101号同学年龄相同的学生的学号、姓名和所在系。
(3)带有ANY或ALL谓词的子查询
使用ANY或ALL谓词时必须同时使用比较运算符。ANY指某一个,ALL指所有。
[例33]查询其他系中比计算机系某一学生年龄小的学生的学号、姓名和年龄,并按年龄降序排列。
ORDER BY Sage DESC;
(4)带有EXISTS谓词的子查询
EXISTS是存在量词。带有EXISTS谓词的子查询不返回任何实际数据,只产生逻辑真值或逻辑假值。
[例34]查询所有选修了1号课程的学生的学号和姓名。
使用EXISTS后,若内层查询结果非空,则外层的WHERE子句返回真值,否则返回假值。由EXISTS引出的子查询,其目标表达式通常都用“*”,因为带EXISTS的子查询只返回真值或假值,给出列名并无实际意义。
也可以使用NOT EXISTS,此时若内层查询结果为空,则外层的WHERE子句返回真值,否则返回假值。
4)集合查询
用集合查询可以把多个SELECT语句的结果合并为一个结果。集合查询主要包括UNION(并)、INTERSECT(交)和MINUS(差)或EXCEPT操作。
使用UNION将多个查询结果合并起来形成一个完整的查询结果时,系统会自动去掉重复的元组。但要求UNION操作的各数据项数目必须相同,对应项的数据类型也必须相同。
[例35]查询计算机系中年龄不大于19岁的学生。
SELECT*
FROM Student
WHERE Sdept=′计算机′
UNION
SELECT*
FROM Student
WHERE Sage<=19;