SQL 单表查询

本文最后更新于:2024年3月18日 凌晨

SQL 单表查询

查询指定列

  • 在很多情况下,用户只对表中的一部分属性列感兴趣,这时可以通过在SELECT子句的<目标列表达式>中指定要查询的属性列。

[例3.16]:查询全体学生的学号与姓名。

1
2
SELECT Sno,Sname
FROM Student;
  • 该语句的执行过程可以是这样的:从Student表中取出一个元组,取出该元组在属性Sno和Sname上的值,形成一个新的元组作为输出,对Student表中的所有元组做相同的处理,最后形成一个结果关系作为输出。

    [例3.17]:查询全体学生的姓名,学号,所在系。

1
2
SELECT Sname,Sno,Sdept
FROM Student;
  • <目标列表达式>中各个列的先后顺序可以与表中的顺序不一致,用户可以根据应用的需要改变列的显示顺序,本例中先列出姓名,再列出学号和所在系。

查询全部列

  • 将表中的所有属性列都选出来有两种方法,一种方法就是在SELECT关键字后列出所有列名;如果列的显示顺序与其在基表中的顺序相同,也可以简单地将<目标列表达式>指定为*

[例3.18]:查询全体学生的详细记录。

1
2
SELECT *
FROM Student;
  • 等价于。
1
2
SELECT Sno,Sname,Ssex,Sage,Sdept
FROM Student;

LIMIT

  • 在使用数据库过程中,常会遇到查询或者导出某个数据表或者查询集的前几条或者后几条记录,LIMIT可以很好的满足需求。
1
SELECT * FROM table  LIMIT [offset,] rows | rows OFFSET offset;

实例

1
2
3
4
5
6
7
SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15

#为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:
SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last

#如果只给定一个参数,它表示返回最大的记录行数目:
SELECT * FROM table LIMIT 5; // 检索前 5 个记录行。
  • 另一种表示方法。
1
2
3
mysql> SELECT * FROM orange LIMIT 2 OFFSET 3;// 查询4-5两条记录。
#等价于
mysql> SELECT * FROM orange LIMIT 3,2;

查询经过计算的值。

  • SELECT子句的<目标列表达式>不仅可以是表中的属性列,也可以是表达式。

[例3.19]:查询全体学生的姓名及其出生年份。

1
2
SELECT Sname,2014-Sage /*查询结果的第2列是一个算术表达式*/
FROM Student;
  • 查询结果中第2列不是列名而是一个计算表达式,是用当时的年份(假设为2014年)减去学生的年龄,这样所得的即使学生额出生年份,输出的结果为:
Sname 2014-Sage
李勇 1994
李晨 1995
王敏 1996
张立 1995
  • <目标列表达式>不仅可以是算术表达式,还可以是字符串常量,函数等。

[例3.20]:查询全体学生的姓名,出生年份和所在额院系,要求用小写字母表示系名。

1
2
SELECT Sname,'Year of Birth:',2014-Sage,LOWER(Sdept)
FROM Student;
  • 结果为。
Sname ‘Year of Birth’ 2014-Sage LOWER(Sdept)
李勇 Year of Birth 1994 cs
刘晨 Year of Birth 1995 cs
王敏 Year of Birth 1996 ma
张立 Year of Birth 1995 is
  • 用户可以通过指定别名来改变查询结果的列标题,这对含算术表达式,常量,函数名的目标列表达式尤为有用,例如对于**[例3.20]**:可以定义如下列别名:
1
SELECT Sname NAME,'Year of Birth:' BIRTH,2014-Sage BIRTHDAY,LOWER(Sdept) DEPARTMENT
  • 结果为。
NAME BIRTH BIRTHDAY DEPARTMENT
李勇 Year of Birth 1994 cs
刘晨 Year of Birth 1995 cs
王敏 Year of Birth 1996 ma
张立 Year of Birth 1995 is

DISTINCT

  • 两个本来并不完全相同的元组在投影到指定的某些列上后,可能会变成相同的行,可以用DISTINCT消除它们。
  • 如果没有指定DISTINCT关键词,则默认为ALL,既保留结果表中取值重复的行。

[例3.21]:查询选修了课程的学生学号。

Sno
201215121
201215121
201215121
201215122
201215122
  • 该查询结果里包含了许多重复的行,如想去掉结果表中的重复行,必须指定DISTINCT
1
2
SELECT DISTINCT Sno
FROM SC
  • 则执行结果为。
Sno
201215121
201215122

WHERE

  • 查询满足指定条件的元组可以通过WHERE子句实现。
查询条件 谓词
比较 =,>,<,>=,<=,!=,<>,!>,!<;NOT+上述比较运算符
确定范围 BETWEEN AND,NOT BETWEEN AND
确定集合 IN,NOT IN
字符匹配 LIKE,NOT LIKE
空值 IS NULL,IS NOT NULL
多重条件(逻辑运算) AND,OR,NOT

比较大小

  • 用于进行比较的运算符一般包括=(等于),>(大于),<(小于),>=(大于等于),<=(小于等于),!=或<>(不等于)``,!>(不大于),!<(不小于)

[例3.22]:查询计算机科学系全体学生的名单。

1
2
3
SELECT Sname
FROM Student
WHERE Sdept='CS';
  • 关系数据库管理系统执行该查询的一种可能过程是:对Student表进行全表扫描,取出一个元组,检查该元组在Sdept列的值是否等于’CS’,如果相等,则取出Sname列的值形成一个新的元组输出,否则跳过该元组,重复该过程,直到处理完Student表的所有元组。
  • 如果全校有数万个学生,计算机系的学生人数是全校学生的5%左右,可以在Student表的Sdept列上建立索引,系统会利用该索引找出Sdept='CS’的元组,从中取出Sname列值形成结果关系,这就避免了对Student表的全表扫描,加快了查询速度,注意如果学生较少,索引查找不一定能提高查询效率,系统仍会使用全表扫描,这由查询优化器按照某些规则或估计执行代价来作出选择。

[例3.23]:查询所有年龄在20岁以下的学生姓名及其年龄。

1
2
3
SELECT Sname,Sage
FROM Student
WHERE Sage<20;

[例3.24]:查询考试成绩不及格的学生的学号。

1
2
3
SELECT DISTINCT Sno
FROM SC
WHERE Grade<60;
  • 这里使用 DISTINCT短语,当一个学生有多门课程不及格,他的学号也只列一次。

确定范围

  • 谓词BETWEEN…AND,,和NOT BETWEEN…AND…可以用来查找属性值在(或不在)指定范围内的元组,其中BETWEEN后是范围的下限(即低值),AND后是范围的上限(即最高值)

[例3.25]:查询年龄在20~23岁(包括20岁和23岁)之间的学生的姓名,系别和年龄。

1
2
3
SELECT Sname,Sdept,Sage
FROM Student
WHERE Sage BETWEEN 20 AND 23;

[例3.26]:查询年龄不在20~23之间的学生姓名,系别和年龄。

1
2
3
SELECT Sname,Sdept,Sage
FROM Student
WHERE Sage NOT BETWEEN 20 AND 23;

确定集合

  • 谓词IN可以用来查找属性值属于指定集合的元组。

[例3.27]:查询计算机科学系(CS),数学系(MA)和信息系(IS)学生的姓名和性别。

1
2
3
SELECT Sname,Ssex
FROM Student
WHERE Sdept IN('CS','MA','IS');
  • 与IN相对的谓词NOT IN,用与查找属性值不属于指定集合的元组。

[例3.28]:查询既不是计算机科学系,数学系,也不是信息系的学生的姓名和性别。

1
2
3
SELECT Sname,Ssex
FROM Student
WHERE Sdept NOT IN ('CS','MA','IS');

字符匹配

  • 谓词LIKE可以用来进行字符串的匹配,其一般语法格式如下:
1
[NOT] LIKE '<匹配串>' [ESCAPE '<换码字符>']
  • 其含义是查找指定的属性列值与<匹配串>相匹配的元组.<匹配串>可以是一个完整的字符串,也可以含有通配符%和_ 其中:
    • %(百分号)代表任意长度(长度可以为0)的字符串,例如a%b表示以a开头,以b结尾的任意长度的字符串,如acb,addgb,ab等都满足该匹配串。
    • _(下横线)代表任意单个字符,例如a_b表示以a开头,以b结尾的长度为3的任意字符串,如acb,afb等都满足该匹配串。

[例3.29]:查询学号为201215121的学生的详细情况。

1
2
3
SELECT *
FROM Student
WHERE Sno LIKE '201215121';
  • 等价于。
1
2
3
SELECT *
FROM Student
WHERE Sno='201215121';
  • 如果LIKE后面额匹配串中不含通配符,则可以用=(等于)运算符取代LIKE谓词,用!=或<>(不等于)运算符取代NOT LIKE谓词。

[例3.30]:查询所有姓刘的学生的姓名,学号和性别。

1
2
3
SELECT Sname,Sno,Ssex
FROM Student
WHERE Sname LIKE '刘%';

[例3.31]:查询姓"欧阳"且全民为三个汉字的学生的姓名。

1
2
3
SELECT Sname
FROM Student
WHERE Sname LIKE '欧阳_';
  • 数据库字符集为ASCII时一个汉字需要两个_;当字符集为GBK时只一个_

[例3.32]:查询名字中第二个字为"阳"的学生的姓名和学号。

1
2
3
SELECT Sname,Sno
FROM Student
WHERE Sname LIKE '_阳%';

[例3.33]:查询所有不姓刘的学生的姓名,学号和性别。

1
2
3
SELECT Sname,Sno,Ssex
FROM Student
WHERE Sname NOT LIKE '刘%';
  • 如果用户要查询的字符串本身就含有通配符%或_,这时就要使用ESCAPE’<换码字符>‘短语对通配符进行转义,例如:ESCAPE’'表示""为换码字符,这样匹配串中紧跟在""后面的字符"_“不再具有通配符的含义,转义为普通的”_"字符。

[例3.34]:查询DB_Design课程的课程号和学分。

1
2
3
SELECT Cno,Ccredit
FROM Course
WHERE Cname LIKE 'DB\_Design' ESCAPE'\';
  • ESCAPE’\'表示”\“为换码字符,这样匹配串中紧跟在”"后面的字符"_“不再具有通配符的含义,转义为普通的”_"字符。

[例3.35]:查询以"DB_"开头,且倒数第三个字符为i的课程的详细情况。

1
2
3
SELECT *
FROM Course
WHERE Cname LIKE 'DB\_%i__'ESCAPE'\';
  • 这里的匹配串为"DB\_%i__",第一次_前面有换码字符\,所以它被转义为普通的_字符,而i后面的两个_的前面均没有换码字符\,所有它们仍作为通配符。
  • [ ] :表示括号内所列字符中的一个(类似正则表达式),指定一个字符,字符串或范围,要求所匹配对象为它们中的任一个。
1
SELECT * FROM [user] WHERE u_name LIKE '[张李王]三';
  • 将找出"张三”,"李三”,"王三”(而不是"张李王三”)
  • [ ] 内有一系列字符(01234,abcde之类的)则可略写为"0-4”,"a-e”
1
SELECT * FROM [user] WHERE u_name LIKE '老[1-9]';
  • 将找出"老1”,"老2”,…,"老9”
  • [^ ] :表示不在括号所列之内的单个字符,其取值和 [ ] 相同,但它要求所匹配对象为指定字符以外的任一个字符。
1
SELECT * FROM [user] WHERE u_name LIKE ' [^张李王]三';
  • 将找出不姓"张”,"李”,"王”的"赵三”,"孙三”等。
1
SELECT * FROM [user] WHERE u_name LIKE '老 [^1-4]';
  • 将排除"老1”到"老4”,寻找"老5”,"老6”,…

涉及空值的查询

[例3.36]:某些学生选修课程后没有参加考试,所以有选课记录,但没有考试成绩,查询缺少成绩的学生的学号和相应的课程号。

1
2
3
SELECT Sno,Cno
FROM SC
WHERE Grade IS NULL;/*分数Grade是空值*/
  • 注意这里的’IS’不能用等号(=)代替。

[例3.37]:查所有有成绩的学生学号和课程号。

1
2
3
SELECT Sno,Cno
FROM SC
WHERE Grade IS NOT NULL;

多重条件查询

  • 逻辑运算符AND和OR可用来连接多个查询条件,AND的优先级高于OR,但用户可以用括号改变优先级。

[例3.38]:查询计算机科学系年龄在20岁以下的学生姓名。

1
2
3
SELECT Sname
FROM Student
WHERE Sdept='CS' AND Sage<20;
  • 在例3.27中的IN谓词实际上是多个OR运算符的缩写,因此该例中的查询也可以用OR运算符写成如下等价形式:
1
2
3
SELECT Sname,Ssex
FROM Student
WHERE Sdept='CS' OR Sdept='MA' OR Sdept='IS';

ORDER BY

  • 用户可以用ORDER BY子句对查询结果按照一个或多个属性列的升序(ASC)降序(DESC)排序,默认值为升序。

[例3.39]:查询选修了3号课程的学生额学号及其成绩,查询结果按分数额降序排列。

1
2
3
4
SELECT Sno,Grade
FROM SC
WHERE Cno='3';
ORDER BY Grade DESC;
  • 对于空值,排序时显示的次序由具体系统实现来决定,例如按升序排,含空值的元组最后显示;按降序排,空值的元组则最先显示。各个系统的实现可以不同,只要保持一致就行。

[例3.40]:查询全体学生情况,查询结果按所在系的系号升序排列,=,同一系中的学生按年龄降序排序。

1
2
3
SELECT *
FORM Student
ORDER BY Sdept,Sage,DESC;
  • 在SQL SERVER中NULL为最小值。

GROUP BY

  • GROUP BY子句将查询结果按某一列或多列的值分组,值相等的为一组。
  • 对查询结果分组的目的是为了细化聚集函数的作用对象,如果未对查询结果分组,聚集函数将作用于整个查询结果,分组后聚集函数将作用于每一个组,即每一组都有一个函数值。

[例3.46]:求各个课程号及相应的选课人数。

1
2
3
SELECT Cno,COUNT(Sno)
FROM SC
GROUP BY Cno;
  • 该语句对查询结果按Cno的值分组,所有具有相同Cno值的元组为一组,然后对每一组作用聚集函数COUNT进行计算,以求得该组的学生人数。
  • 查询结果可能为:
Cno COUNT(Sno)
1 22
2 34
3 44
4 33
5 48
  • 如果分组后还要按一定的条件对这些组进行筛选,最终只输出满足指定条件的组,则可以使用HAVING短语指定筛选条件。

[例3.48]:查询平均成绩大于等于90分的学生学号和平均成绩。

1
2
3
4
SELECT Sno,AVG(Grade)
FROM SC
GROUP BY Sno
HAVING AVG(Grade)>=90;
  • 这里先用GROUP BY子句按Sno进行分组,再用聚集函数AVG对每一组求平均;HAVING短语给出了选择组的条件,只有满足条件的组才会选出来。
  • WHERE子句与HAVING短语的区别在于作用对象不同,WHERE子句作用于基本表或视图,从中选择满足条件的元组,HAVING短语作用于组,从中选择满足条件的组。

聚集函数

查询语句 说明
COUNT() 统计元组个数
COUNT([DISTINCT/ALL]<列名>) 统计一列中值的个数
SUM([DISTINCT/ALL]<列名>) 统计一列中值的总和
AVG([DISTINCT/ALL]<列名>) 计算一列值的平均值(此列必须是数值型)
MAX([DISTINCT/ALL]<列名>) 求一列值中的最大值
MIN([DISTINCT/ALL]<列名>) 求一列值中的最小值
GROUP_CONCAT() 将组中的字符串连接成为具有各种选项的单个字符串
  • 如果指定DISTINCT短语,则表示在计算时要取消指定列中的重复值,如果不指定DISTINCT短语或指定ALL短语(ALL为默认值),则表示不取消重复值。
  • WHERE子句中是不能用聚集函数作为条件表达式的,聚集函数只能用于SELECT子句和GROUP BY中的HAVING子句。

[例3.41]:查询学生总人数。

1
2
SELECT COUNT(*)
FROM Student;
  • 当聚集函数遇到空值时,除COUNT(*)外,都跳过空值而只处理非空值,COUNT(*)是对元组进行计数,某个元组的一个或部分列取空值不影响COUNT的统计结果。

[例3.42]:查询选修了课程的学生人数。

1
2
SELECT COUNT(DISTINCT Sno)
FROM SC;
  • 学生每选修一门课,在SC中都有一条相应记录,一个学生要选修多门课程,为避免重复计算学生人数,必须在COUNT函数中用DISTINCT短语。

[例3.43]:计算选修1号课程的学生平均成绩。

1
2
3
SELECT AVG(Grade)
FROM SC
WHERE Cno='1';

[例3.44]:查询选修1号课程的学生最高分数。

1
2
3
SELECT MAX(Grade)
FROM SC
WHERE Cno='1';

[例3.45]:查询学生201215012选修课程的总学分数。

1
2
3
SELECT SUM(Ccredit)
FROM SC,Course
WHERE Sno='201215012' AND SC.Cno=Course.Cno;

GROUP_CONCAT()

1
2
3
4
5
SELECT
GROUP_CONCAT(DISTINCT country
ORDER BY country
SEPARATOR ';')
FROM CountryTable
  • DISTINCT子句用于在连接分组之前消除组中的重复值。

  • ORDER BY子句允许您在连接之前按升序或降序排序值,默认情况下,它按升序排序值,如果要按降序对值进行排序,则需要明确指定DESC选项。

  • SEPARATOR指定在组中的值之间插入的文字值,如果不指定分隔符,则GROUP_CONCAT函数使用逗号(,)作为默认分隔符。

  • GROUP_CONCAT函数忽略NULL值,如果找不到匹配的行,或者所有参数都为NULL值,则返回NULL


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!