数据查询
语句格式
SELECT [ALL|DISTINCT] <目标列表达式> [,<目标列表达式>] … FROM <表或视图名>[,<表或视图名> ] … [ WHERE <条件表达式> ] [ GROUP BY <列名1> [ HAVING <条件表达式> ] ] [ ORDER BY <列名2> [ ASC|DESC ] ];
SELECT子句:指定要显示的属性列
FROM子句:指定查询对象(基本表或视图)
WHERE子句:指定查询条件
GROUP BY子句:对查询结果按指定列的值分组,该属性列值相等的元组为一个组。通常会在每组中作用集函数。
HAVING短语:筛选出只有满足指定条件的组
ORDER BY子句:对查询结果表按指定列值的升序或降序排序
示例数据库
学生表:
Student(Sno,Sname,Ssex,Sage,Sdept)
课程表:
Course(Cno,Cname,Cpno,Ccredit)
选课表:
SC(Sno,Cno,Grade)
1.单表查询
查询仅涉及一个表,是一种最简单的查询操作
一、选择表中的若干列
二、选择表中的若干元组
三、对查询结果排序
四、使用集函数
五、对查询结果分组
一、选择表中的若干列
1查询指定列
[例1] 查询全体学生的学号与姓名。
SELECT Sno,Sname
FROM Student;
[例2] 查询全体学生的姓名、学号、所在系。
SELECT Sname,Sno,Sdept
FROM Student;
语句不分大小写,一般分行写,分号可不写
2查询全部列
[例3] 查询全体学生的详细记录。
SELECT Sno,Sname,Ssex,Sage,Sdept FROM Student; 或 SELECT * FROM Student;
3. 查询经过计算的值
[例4] 查全体学生的姓名及其出生年份。
SELECT Sname,2015-Sage FROM Student;
输出结果:
Sname 2015-Sage
--------- -------------
李勇 1986
刘晨 1987
王名 1988
张立 1988
[例5] 查询全体学生的姓名、出生年份和所有系,要求用小写字母表示所有系名。
SELECT Sname,'Year of Birth: ',2015- Sage, ISLOWER(Sdept) FROM Student;
字符串常量用单引号
输出结果:
Sname 'Year of Birth:' 2015-Sage ISLOWER(Sdept)
------- ------------ ------- -----------
李勇 Year of Birth: 1986 cs
刘晨 Year of Birth: 1987 is
王名 Year of Birth: 1988 ma
张立 Year of Birth: 1987 is
ISLOWER是ORACLE的函数名
SQL-SERVER的函数名是LOWER
SELECT Sname,'Year of Birth: ',2015 -Sage, LOWER(Sdept) FROM Student;
[例5.1] 使用列别名(可选)改变查询结果的列标题
SELECT Sname NAME,'Year of Birth: ' BIRTH, 2015-Sage BIRTHDAY,LOWER(Sdept) DEPARTMENT FROM Student;
输出结果:
NAME BIRTH BIRTHDAY DEPARTMENT
------- ---------------- ---------- -------------
李勇 Year of Birth: 1986 cs
刘晨 Year of Birth: 1987 is
王名 Year of Birth: 1988 ma
张立 Year of Birth: 1987 is
二、选择表中的若干元组
1. 消除取值重复的行
在SELECT子句中使用DISTINCT短语
假设SC表中有下列数据
Sno Cno Grade
------- ------- -------
95001 1 92
95001 2 85
95001 3 88
95002 2 90
95002 3 80
[例6] 查询选修了课程的学生学号。
(1)
SELECT Sno
FROM SC;
结果: Sno
-------
95001
95001
95001
95002
95002
(2)
SELECT DISTINCT Sno
FROM SC;
结果:
Sno
-------
95001
95002
2.查询满足条件的元组
(1) 比较大小
在WHERE子句的<比较条件>中使用比较运算符
=,>,<,>=,<=,!= 或 <>,!>,!<,
逻辑运算符NOT
[例8] 查询所有年龄在20岁以下的学生姓名及其年龄。
SELECT Sname,Sage FROM Student WHERE Sage < 20;
[例] 查询计算机系的学生姓名及其年龄。
SELECT Sname,Sage FROM Student WHERE Sdept='CS';
(2) 确定范围
使用谓词 BETWEEN … AND …
NOT BETWEEN … AND …
[例10] 查询年龄在20~23岁(包括20岁和23岁)之间的学生的姓名、系别和年龄。
SELECT Sname,Sdept,Sage FROM Student WHERE Sage BETWEEN 20 AND 23;
[例11] 查询年龄不在20~23岁之间的学生姓名、系别和年龄。
SELECT Sname,Sdept,Sage FROM Student WHERE Sage NOT BETWEEN 20 AND 23;
(3) 确定集合
使用谓词 IN (值表), NOT IN (值表)
值表:用逗号分隔的一组取值
[例12]查询信息系(IS)、数学系(MA)和计算机科学系(CS)学生的姓名和性别。
SELECT Sname,Ssex FROM Student WHERE Sdept IN ( 'IS','MA','CS' );
[例13]查询既不是信息系、数学系,也不是计算机科学系的学生的姓名和性别。
SELECT Sname,Ssex FROM Student WHERE Sdept NOT IN ( 'IS','MA','CS' );
(4) 字符串匹配
[NOT] LIKE ‘<匹配串>’
<匹配串>:含精确匹配字符和通配符
模糊匹配---通配符
% (百分号) 代表任意长度(长度可以为0)的字符串
例:a%b表示以a开头,以b结尾的任意长度的字符串。如acb,addgb,ab 等都满足该匹配串
_ (下横线) 代表任意单个字符
例:a_b表示以a开头,以b结尾的长度为3的任意字符串。如acb,afb等都满足该匹配串
1) 匹配串为固定字符串
[例14] 查询姓名为'刘晨'学生的详细情况。
SELECT * FROM Student WHERE Sname LIKE '刘晨'; 等价于: SELECT * FROM Student WHERE Sname = '刘晨';
2) 匹配串为含通配符的字符串
[例15] 查询所有姓刘学生的姓名、学号和性别。
SELECT Sname,Sno,Ssex FROM Student WHERE Sname LIKE '刘%';
[例16] 查询姓"欧阳"且全名为三个字的学生的姓名。
SELECT Sname FROM Student WHERE Sname LIKE '欧阳_';
[例17] 查询名字中第2个字为‘阳’字的学生的姓名和学号。
SELECT Sname,Sno FROM Student WHERE Sname LIKE '_阳%';
[例18] 查询所有不姓刘的学生姓名。
SELECT Sname,Sno,Ssex FROM Student WHERE Sname NOT LIKE '刘%';
(5) 涉及空值的查询
使用谓词 IS NULL 或 IS NOT NULL
“IS NULL” 不能用 “= NULL” 代替
[例21] 某些学生选修课程后没有参加考试,所以有选课记录,但没有考试成绩。查询缺少成绩的学生的学号和相应的课程号。
SELECT Sno,Cno
FROM SC
WHERE Grade IS NULL;
[例22] 查所有有成绩的学生学号和课程号。
SELECT Sno,Cno
FROM SC
WHERE Grade IS NOT NULL;
(6) 多重条件查询
用逻辑运算符AND和 OR来联结多个查询条件
AND的优先级高于OR
[例23] 查询计算机系年龄在20岁以下的学生姓名。
SELECT Sname FROM Student WHERE Sdept= 'CS' AND Sage<20;
改写[例12]
例12] 查询信息系(IS)、数学系(MA)和计算机科学系(CS)学生的姓名和性别。
SELECT Sname,Ssex FROM Student WHERE Sdept IN ( 'IS','MA','CS' )
可改写为:
SELECT Sname,Ssex FROM Student WHERE Sdept= ' IS ' OR Sdept= ' MA' OR Sdept= ' CS ';
改写[例10]
[例10] 查询年龄在20~23岁(包括20岁和23岁)之间的学生的姓名、系别和年龄。
SELECT Sname,Sdept,Sage FROM Student WHERE Sage BETWEEN 20 AND 23;
可改写为:
SELECT Sname,Sdept,Sage FROM Student WHERE Sage>=20 AND Sage<=23;
三、对查询结果排序
使用ORDER BY子句
可以按一个或多个属性列排序
升序:ASC;(缺省)
降序:DESC;
[例24] 查询选修了3号课程的学生的学号及其成绩,查询结果按分数降序排列。
SELECT Sno,Grade FROM SC WHERE Cno= ' 3 ' ORDER BY Grade DESC;
[例25] 查询全体学生情况,查询结果按所在系的系号升序排列,同一系中的学生按年龄降序排列。
SELECT *
FROM Student
ORDER BY Sdept,Sage DESC;
四、使用集函数
5类主要集函数
1. 行计数COUNT
2. 计算总和SUM
3. 计算平均值AVG
4. 求最大值 MAX
5. 求最小值MIN
行计数的两种形式
(1) COUNT(*)
对每行计数
[例26] 查询学生总人数。
SELECT COUNT(*)
FROM Student;
查询计算机系的学生人数
SELECT COUNT(*)
FROM Student
WHERE SDEPT=‘CS’
查询1号课不及格人数
SELECT COUNT(*) FROM SC WHERE CNO=1 AND GRADE<60
(2)COUNT( <列名> )
对<列名>非空的行计数
COUNT( [DISTINCT|ALL] <列名> )
DISTINCT:计数时要先取消指定列中的重复值
ALL:不取消重复值,ALL为缺省值
[例27] 查询选修了课程的学生人数。
SELECT COUNT(DISTINCT Sno)
FROM SC;
注:对Sno非空的行,排除重复Sno计数。
以避免重复计算学生人数
有什么区别?
(1)
SELECT COUNT(*) FROM SC; 对SC每行计数
(2)
SELECT COUNT(Sno) FROM SC; Sno非空行计数,因Sno为主属性,所以与(1)相同
(3)
SELECT COUNT(DISTINCT Sno) FROM SC;
SNO去重复行计数
(4)
SELECT COUNT(Grade) FROM SC;
GRADE非空行计数
[例28] 计算1号课程的学生平均成绩。
SELECT AVG(Grade) FROM SC WHERE Cno= ' 1 ';
(成绩为null的行不参与计算)
[例29] 查询选修1号课程的学生最高分数。
SELECT MAX(Grade) FROM SC WHER Cno= ' 1 ';
[例] 计算1号课程的平均成绩,最高分,最低分,选课人数
SELECT AVG(Grade), MAX(Grade),MIN(Grade), COUNT(*) FROM SC WHERE Cno= ' 1 ';
五、对查询结果分组
使用GROUP BY子句分组
细化集函数的作用对象
未对查询结果分组,集函数将作用于整个查询结果
对查询结果分组后,集函数将分别作用于每个组
分组后的聚集函数将作用于每一个组,即每一个组都有一个函数值
同时这个分组是先进行的排序,这也是为什么分组后的结果是按照顺序排列的。
使用GROUP BY子句分组
[例30] 求各个课程号及相应的选课人数,平均成绩。
SELECT Cno,COUNT(*),AVG(Grade)
FROM SC
GROUP BY Cno;
结果
Cno COUNT(*) AVG(Grade)
1 22 75
2 34 80
3 44 78
易犯错误:
SELECT Sno, Cno, COUNT(*),AVG(Grade)--在统计查询中,这里只允许有分组属性和集函数
FROM SC
GROUP BY Cno;
[例30] 求各个课程号及相应的选课人数,平均成绩。
SELECT Cno,COUNT(*) ,AVG(Grade)
FROM SC
GROUP BY Cno;
对具有可分组的列分组,输出这个列和统计信息
用多个列分组
[例31] 查询每个系男女生人数。
SELECT Sdept, Ssex, COUNT(*)
FROM Student
GROUP BY Sdept, Ssex;
如果分组后还要求按一定的条件对这些组进行筛选,最终只输出满足指定条件的组,则使用HAVING短语筛选最终输出结果
[例32] 查询选修了3门以上课程的学生学号。
SELECT Sno FROM SC GROUP BY Sno HAVING COUNT(*) >=3;
[例33] 查询有3门以上课程是90分以上的学生的学号及(90分以上的)课程数
SELECT Sno, COUNT(*) FROM SC WHERE Grade>=90 GROUP BY Sno HAVING COUNT(*)>=3;
WHERE子句与HAVING短句的区别在于作用对象不同。
WHERE子句作用于基本表或视图,从中选择满足条件的元组(表中的一行)。
HAVING短句作用于组,从中选择满足条件的组。