数据库的查询和视图2
4. 计算列值
使用SELECT对列进行查询时,在结果中可以输出对列值计算后的值,即SELECT子句可使用表达式作为结果,格式为:
SELECT expression [ , expression ]
按120分计算成绩,显示XS_KC表中学号为081101的学生课程信息。
SELECT学号, 课程号,
成绩*1.20 AS 成绩120
FROM XS_KC
WHERE 学号= '081101';
该语句的执行结果如下所示:
5. 消除结果集中的重复行
对表只选择其某些列时,可能会出现重复行。例如,若对XSCJ数据库的XS表只选择专业名和总学分,则出现多行重复的情况。可以使用DISTINCT或DISTINCTROW关键字消除结果集中的重复行,其格式是:
SELECT DISTINCT | DISTINCTROW column_name [, column_name…]
其含义是对结果集中的重复行只选择一个,保证行的唯一性。
对XSCJ数据库的XS表只选择专业名和总学分,消除结果集中的重复行。
SELECT DISTINCT 专业名,总学分
FROM XS;
该语句的执行结果为:
6. 聚合函数(aggregation function)
SELECT子句的表达式中还可以包含所谓的聚合函数。聚合函数常常用于对一组值进行计算,然后返回单个值。除COUNT函数外,聚合函数都会忽略空值。聚合函数通常与GROUP BY子句一起使用。如果SELECT语句中有一个GROUP BY子句,则这个聚合函数对所有列起作用,如果没有,则SELECT语句只产生一行作为结果。
表4.10列出了一些常用的聚合函数。
(1)COUNT函数
聚合函数中最经常使用的是COUNT()函数,用于统计组中满足条件的行数或总行数,
返回SELECT语句检索到的行中非NULL值的数目,若找不到匹配的行,则返回0。
语法格式为:
COUNT ( { [ ALL | DISTINCT ] expression } |* )
其中,expression是一个表达式,其数据类型是除BLOB或TEXT之外的任何类型。ALL表示对所有值进行运算,DISTINCT表示去除重复值,默认为ALL。使用COUNT(*)时将返回检索行的总数目,不论其是否包含 NULL值。
求学生的总人数。
SELECT COUNT(*) AS '学生总数'
FROM XS;
执行结果为:
统计备注不为空的学生数目。
SELECT COUNT(备注)AS '备注不为空的学生数目'
FROM XS;
注意:这里COUNT(备注)计算时备注为NULL的行被忽略,所以这里是7而不是22。
统计总学分在50分以上的人数。
SELECT COUNT(总学分) AS '总学分50分以上的人数'
FROM XS
WHERE 总学分>50;
执行结果为:
(2)MAX和MIN
MAX和MIN分别用于求表达式中所有值项的最大值与最小值,语法格式为:
MAX / MIN ( [ ALL | DISTINCT ] expression )
其中,expression是常量、列、函数或表达式,其数据类型可以是数字、字符和时间日期 类型。
求选修101课程的学生的最高分和最低分。
SELECT MAX(成绩), MIN(成绩)
FROM XS_KC
WHERE 课程号 = '101';
执行结果为:
注意:当给定列上只有空值或检索出的中间结果为空时,MAX和MIN函数的值也为空。
(3)SUM函数和AVG函数
SUM和AVG分别用于求表达式中所有值项的总和与平均值,语法格式为:
SUM / AVG ( [ ALL | DISTINCT ] expression )
其中,expression是常量、列、函数或表达式,其数据类型只能是数值型数据。
求学号081101的学生所学课程的总成绩。
SELECT SUM(成绩) AS '课程总成绩'
FROM XS_KC
WHERE 学号 = '081101';
执行结果为:
求选修101课程的学生的平均成绩。
SELECT AVG(成绩) AS '课程101平均成绩'
FROM XS_KC
WHERE 课程号 = '101';
执行结果为:
(4)VARIANCE和STDDEV(STD)函数
VARIANCE和STDDEV函数分别用于计算特定的表达式中的所有值的方差和标准差。语法格式:
VARIANCE / STDDEV ( [ ALL | DISTINCT ]expression )
求选修101课程的成绩的方差。
SELECT VARIANCE(成绩)
FROMXS_KC
WHERE课程号= '101';
执行结果为:
说明:方差的计算按照以下几个步骤进行。
① 计算相关列的平均值;
② 求列中的每一个值和平均值的差;
③ 计算差值的平方的总和;
④ 用总和除以(列中的)值得结果。
STDDEV函数用于计算标准差。标准差等于方差的平均根。所以,STDDEV(…)和SQRT(VARIANCE(…))这两个表达式是相等的。
求选修101课程的成绩的标准差。
SELECT STDDEV(成绩)
FROMXS_KC
WHERE课程号= '101';
执行结果为:
STDDEV可以缩写为STD,这对结果没有影响。
(5)GROUP_CONCAT函数
MySQL支持一个特殊的聚合函数GROUP_CONCAT函数。该函数返回来自一个组指定列的所有非NULL值,这些值一个接着一个放置,中间用逗号隔开,并表示为一个长长的字符串。这个字符串的长度是有限制的,标准值是1024。
语法格式为:
GROUP_CONCAT ( { [ ALL | DISTINCT ]expression } | * )
求选修了206课程的学生的学号。
SELECT GROUP_CONCAT(学号)
FROMXS_KC
WHERE课程号= '206';
执行结果为:
(6)BIT_AND、BIT_OR和BIT_XOR
与二进制运算符|(或)、&(与)和^(异或)相对应的聚合函数也存在,分别是BIT_OR 、BIT_AND、BIT_XOR。例如,函数BIT_OR在一列中的所有值上执行一个二进制OR。
语法格式为:
BIT_AND | BIT_OR | BIT_XOR( { [ ALL |DISTINCT ] expression } | * )
有一个表BITS,其中有一列bin_value上有3个INTEGER值:1、3、7,获取在该列上执行BIT_OR的结果,使用如下语句:
SELECT BIN(BIT_OR(bin_value))
FROM BITS;
说明:MySQL在后台执行如下表达式:(001|011)|111,结果为111。 其中BIN函数用于将结果转换为二进制位。
FROM子句
前面介绍了使用SELECT子句选择列,本小节讨论SELECT查询的对象(即数据源)的构成形式。SELECT的查询对象由FROM子句指定,其格式为:
FROMtable_reference [ , table_reference] …
其中,table_reference:
tbl_name [ [AS] tbl_name_alias ] [{USE |IGNORE | FORCE} INDEX (key_list)] /*查询表*/
| join_table /*连接表*/
说明:table_reference指出了要查询的表或视图。
● tbl_name为要查询的表名,与列别名一样,可以使用AS选项为表指定别名,tbl_name_alias为表指定的别名。表别名主要用在相关子查询及连接查询中。如果FROM子句指定了表别名,这条SELECT语句中的其他子句都必须使用表别名来代替原始的表名。当同一个表在SELECT语句中多次被提到的时候,就必须要使用表别名来加以区分。
● {USE | IGNORE | FORCE} INDEX:USE INDEX告知MySQL选择一个索引来查找表中的行,IGNORE INDEX告知MySQL不要使用某些特定的索引,FORCE INDEX的作用接近USE INDEX(key_list),只有当无法使用一个给定的索引来查找表中的行时,才使用表扫描。
table_reference中可以包含一个或多个表:
● 引用一个表:
可以用两种方式引用一个表,第一种方式是使用USE语句让一个数据库成为当前数据库,在这种情况下,如果在FROM子句中指定表名,则该表应该属于当前数据库。第二种方式是指定的时候在表名前带上表所属数据库的名字。例如,假设当前数据库是db1,现在要显示数据库db2里的表tb的内容,使用如下语句:
SELECT* FROM db2.tb;
当然,在SELECT关键字后指定列名的时候也可以在列名前带上所属数据库和表的名字,但是一般来说,如果选择的字段在各表中是唯一的,就没有必要去特别指定。
从XS表中检索出所有学生的信息,并使用表别名STUDENT。
使用如下语句:
SELECT *
FROM XS AS STUDENT;
● 引用多个表:
如果要在不同表中查询数据,则必须在FROM子句中指定多个表。指定多个表时就要使用到连接。当不同列的数据组合到一个表中叫做表的连接。例如,在XSCJ数据库中需要查找选修了离散数学课程的学生的姓名和成绩,就需要将XS、KC和XS_KC三个表进行连接,才能查找到结果。
连接的方式有以下两种。
1. 全连接
连接的第一种方式是将各个表用逗号分隔,这样就指定了全连接。FROM子句产生的中间结果是一个新表,新表是每个表的每行都与其他表中的每行交叉以产生所有可能的组合,列包含了所有表中出现的列,也就是笛卡儿积。这样连接表潜在地产生数量非常大的行,因为可能得到的行数为每个表中行数之积。在这样的情形下,通常要使用WHERE子句设定条件来将结果集减少为易于管理的大小,这样的连接即为等值连接。
查找XSCJ数据库中所有学生选过的课程名和课程号,使用如下语句:
SELECT DISTINCT KC.课程名, XS_KC.课程号
FROM KC, XS_KC
WHERE KC.课程号=XS_KC.课程号;
执行结果如下:
2. JOIN连接
第二种方式是使用JOIN关键字的连接,join_table中定义了如何使用JOIN关键字连接表。
语法格式如下:
join_table:
table_reference INNER JOIN table_reference [join_condition]
|table_reference{ LEFT | RIGHT} [OUTER] JOIN table_reference join_condition
|table_reference NATURAL [ {RIGHT | LEFT} [OUTER] ] JOIN table_reference
|table_reference CROSS JOIN table_reference [join_condition]
|table_reference STRAIGHT_JOIN table_reference [ON condition_exp]
其中,join_condition:
ONconditional_expr
| USING (column_list)
说明:
table_reference中指定了要连接的表名,ON condition_exp中指定连接条件。
使用JOIN关键字的连接主要分为三种:
(1)内连接
指定了INNER关键字的连接是内连接。
对于例4.21中的连接,FROM子句中产生的中间结果是两个表的笛卡儿积。内连接中FROM子句产生的中间结果是应用了ON条件后的笛卡儿积。
要实现例4.21中的结果,可以使用以下语句:
SELECTDISTINCT 课程名, XS_KC.课程号
FROM KC INNER JOIN XS_KC
ON (KC.课程号=XS_KC.课程号);
该语句根据ON关键字后面的连接条件,合并两个表,返回满足条件的行。
内连接是系统默认的,可以省略INNER关键字。使用内连接后,FROM子句中ON条件主要用来连接表,其他并不属于连接表的条件可以使用WHERE子句来指定。
用FROM子句的JOIN关键字表达下列查询:查找选修了206课程且成绩在80分以上的学生姓名及成绩。
SELECT 姓名,成绩
FROM XS JOIN XS_KC ON XS.学号 = XS_KC.学号
WHERE 课程号 ='206' AND 成绩>=80;
执行结果如下:
内连接还可以用于多个表的连接。
用FROM的JOIN关键字表达下列查询:查找选修了“计算机基础”课程且成绩在80分以上的学生学号、姓名、课程名及成绩。
SELECT XS.学号, 姓名, 课程名, 成绩
FROM XS JOIN XS_KC ON XS.学号 = XS_KC.学号
JOIN KC ON XS_KC.课程号 = KC.课程号
WHERE 课程名 = '计算机基础' AND 成绩>=80 ;
执行结果如下:
作为特例,可以将一个表与它自身进行连接,称为自连接。若要在一个表中查找具有相同列值的行,则可以使用自连接。使用自连接时需为表指定两个别名,且对所有列的引用均要用别名限定。
查找XSCJ数据库中课程不同、成绩相同的学生的学号、课程号和成绩。
SELECT a.学号,a.课程号,b.课程号,a.成绩
FROM XS_KC AS a JOIN XS_KC AS b
ON a.成绩=b.成绩 AND a.学号=b.学号 AND a.课程号!=b.课程号;
执行结果为:
如果要连接的表中有列名相同,并且连接的条件就是列名相等,那么ON条件也可以换成USING子句。USING(column_list)子句用于为一系列的列进行命名。这些列必须同时在两个表中存在。其中column_list为两表中相同的列名。
查找KC表中所有学生选过的课程名。
SELECT课程名
FROM KC INNER JOIN XS_KC
USING (课程号);
说明:查询的结果为XS_KC表中所有出现的课程号对应的课程名。
(2)外连接
指定了OUTER关键字的连接为外连接。
外连接包括:
● 左外连接(LEFT OUTER JOIN):结果表中除了匹配行外,还包括左表有的但右表中不匹配的行,对于这样的行,从右表被选择的列设置为NULL。
● 右外连接(RIGHT OUTER JOIN):结果表中除了匹配行外,还包括右表有的但左表中不匹配的行,对于这样的行,从左表被选择的列设置为NULL。
● 自然连接(NATURAL JOIN):自然连接还有自然左外连接(NATURAL LEFT OUTER JOIN)和自然右外连接(NATURAL RIGHT OUTER JOIN)。NATURAL JOIN的语义定义与使用了ON条件的INNER JOIN相同。
其中的OUTER关键字均可省略。
查找所有学生情况及他们选修的课程号,若学生未选修任何课,也要包括其情况。
SELECT XS.* , 课程号
FROM XS LEFT OUTER JOIN XS_KC ON XS.学号 = XS_KC.学号;
说明:若本例不使用LEFTOUTER JOIN,则结果中不会包含未选任何课程的学生信息。使用了左外连接后,本例结果中返回的行中有未选任何课程的学生信息,相应行的课程号字段值为NULL。
查找被选修了的课程的选修情况和所有开设的课程名。
SELECT XS_KC.* , 课程名
FROM XS_KC RIGHT JOIN KC ON XS_KC.课程号= KC.课程号;
说明:本例执行时,若某课程未被选修,则结果表中相应行的学号、课程号和成绩字段值均为NULL。
使用自然连接实现例4.22中相同的结果。
SELECT DISTINCT 课程名, XS_KC.课程号
FROM KC NATURAL JOIN XS_KC;
说明:SELECT语句中只选取一个用来连接表的列时,可以使用自然连接代替内连接。用这种方法,可以用自然左外连接来替换左外连接,自然右外连接替换右外连接。
注意:外连接只能对两个表进行。
(3)交叉连接
指定了CROSS JOIN关键字的连接是交叉连接。
在不包含连接条件,交叉连接实际上是将两个表进行笛卡儿积运算,结果表是由第一个表的每行与第二个表的每一行拼接后形成的表,因此结果表的行数等于两个表行数之积。
在MySQL中,CROSS JOIN从语法上来说与INNER JOIN等同,两者可以互换。
列出学生所有可能的选课情况。
SELECT 学号, 姓名, 课程号, 课程名
FROM XS CROSS JOIN KC;
另外,STRAIGHT_JOIN连接用法和INNER JOIN连接基本相同。不同的是,STRAIGHT_JOIN后不可以使用USING子句替代ON条件。
使用STRAIGHT_JOIN连接实现例4.22中相同的结果。
SELECTDISTINCT 课程名, XS_KC.课程号
FROM KC STRAIGHT_JOIN XS_KC
ON (KC.课程号=XS_KC.课程号);