文章目录
- DQL数据查询语言
- 语法:
- 基础查询
- 语法:
- WHERE子句
- 使用AND"与"和OR"或"来连接多个条件进行查询
- IN(列表):等于列表其中之一(在列表中)
- NOT IN(列表):不在列表中,不能等于列表中的所有项
- BETWEEN...AND...:在一个范围之内
- DISTINCT去重操作。在结果集中去除指定字段值相同的记录
- LIKE 模糊查询
- NULL值判断
- ORDER BY子句
- 分页查询
- 语法:
- DQL中可以使用函数或者表达式进行查询
- 使用表达式
- 使用函数
- 在WHERE子句中使用函数或表达式作为过滤条件
- 使用表达式
- 使用函数
- 别名
- 语法
- 聚合函数
- GROUP BY子句,分组
- 按照单字段分组
- 按照多字段分组
- 按照聚合函数排序
- HAVING子句,在分组统计时添加过滤条件
- 聚合函数不能被应用与WHERE子句
- HAVING子句
- 子查询
- 子查询使用场景
- 子查询分类
- 在DQL语句中使用子查询
- 在DML中使用子查询
- 在DDL中使用子查询
- 关联查询(重点知识)
- 笛卡尔积
- 关联查询语法:
- 关联查询中可以指定过滤条件
- N张表关联查询至少要有N-1个连接条件
- HAVING和WHERE的区别
DQL数据查询语言
DQL是用于查询表中数据的语言。
语法:
SELECT 子句
FROM 子句
WHERE 子句
JOIN...ON...子句
GROUP BY 子句
HAVING 子句
ORDER BY子句
一条DQL语句至少要包含的两个子句分别是SELECT子句和FROM子句
- SELECT子句用于指定查询的字段名称。选定的字段会体现在查询结果集中
- FROM子句用于指定数据来自那些表(指定查询的表)
基础查询
由SELECT和FROM构成。
语法:
-
SELECT子句和FROM子句
SELECT 字段1[,字段2,字段3,...] FROM 表1[,表2,表3...]
-
SELECT子句中可以使用*表达查询所有的字段
实际开发中不推荐使用,因为查询低效*
例
查询teacher表中的所有数据
SELECT * FROM teacher
上述SQL执行时,数据库会先解析"*",这个操作会导致数据库先查看内部数据字典了解表的字段后
才能查看表中数据。由于查询是非常频繁的操作,因此每次查看数据字段无疑是浪费性能且消耗时间的操作!
全字段查询时应当在SELECT子句中将所有字段全部列出来(后期java程序执行时)。
如果是手动临时查询,可以使用*。
查看每个老师的名字,职称,工资,性别
SELECT name,title,salary,gender
FROM teacher
WHERE子句
在DQL中用于筛选查询的记录。最终数据库仅将满足WHERE子句条件的记录体现在结果集中。
例
-
查看职称为"一级讲师"的老师名字和工资?
1:查询的是老师信息,能确定FROM子句中的表名为teacher 2:查看的是老师的名字和工资,能确定SELECT子句中的字段name和salary 3:由于仅查看"一级讲师",能确定WHERE子句过滤条件为title='一级讲师' SELECT name,salary,title FROM teacher WHERE title='一级讲师'
-
查看除’刘苍松’老师以外的其他老师的名字,工资和年龄?
SELECT name,salary,age FROM teacher WHERE name<>'刘苍松'
-
查看所有职位是’大队长’的同学的名字年龄和性别
SELECT name,age,gender FROM student WHERE job='大队长'
-
查看年龄在30(含)岁以上的老师都有谁?查看老师的名字,年龄,性别,工资
SELECT name,age,gender,salary FROM teacher WHERE age>=30
使用AND"与"和OR"或"来连接多个条件进行查询
- AND:都为真时才为真
- OR:都为假时才为假
例
-
查看7岁的大队长都是谁?列出:名字,年龄,性别,职位
SELECT name,age,gender,job FROM student WHERE job='大队长' AND age=7
-
查看班级编号小于6的所有中队长的名字,年龄,性别,职位和所在班级编号
SELECT name,age,gender,job,class_id FROM student WHERE class_id<6 AND job='中队长'
-
查看所有一级讲师和三级讲师的名字,职称,工资?
SELECT name,title,salary FROM teacher WHERE title='一级讲师' OR title='三级讲师'
-
查看所有大队长,中队长和小队长的名字,年龄,性别
SELECT name,age,gender FROM student WHERE job='大队长' OR job='中队长' OR job='小队长'
-
查看班级编号在6(含)以下的所有大队长和中队长的名字,年龄,职位,班级编号
AND的优先级是高于OR的
可以使用()提高优先级
SELECT name,age,job,class_id FROM student WHERE class_id<=6 AND job='大队长' OR job='中队长' 上述SQL的条件应当读作:查看班级编号6以下的大队长和所有班级编号的中队长 SELECT name,age,job,class_id FROM student WHERE class_id<=6 AND (job='大队长' OR job='中队长') 提高OR的优先级满足查询要求
IN(列表):等于列表其中之一(在列表中)
例
-
查看所有的大队长,中队长,小队长的名字,年龄,性别
SELECT name,age,gender FROM student WHERE job='大队长' OR job='中队长' OR job='小队长' 等价于 SELECT name,age,gender FROM student WHERE job IN('大队长','中队长','小队长')
-
查看所有一级讲师,二级讲师,三级讲师的老师名字,职称和工资?
SELECT name,title,salary FROM teacher WHERE title IN ('一级讲师','二级讲师','三级讲师')
NOT IN(列表):不在列表中,不能等于列表中的所有项
- 查看一级讲师和二级讲师以外的所有老师的名字,职称,工资?
SELECT name,title,salary
FROM teacher
WHERE title NOT IN('一级讲师','二级讲师')
-
查看不是中队长和大队长以及小队长的其他学生的名字,年龄,职位
SELECT name,age,job FROM student WHERE job NOT IN('大队长','中队长','小队长')
BETWEEN…AND…:在一个范围之内
-
查看工资在2000-5000之间的老师的名字,工资,职称?
SELECT name,salary,title FROM teacher WHERE salary>=2000 AND salary<=5000 等价于 SELECT name,salary,title FROM teacher WHERE salary BETWEEN 2000 AND 5000 下限 上限
-
查看年龄在7-10岁之间的学生的名字,年龄,性别
SELECT name,age,gender FROM student WHERE age BETWEEN 7 AND 10
-
查看年龄在20-35岁之间的男老师的名字,职称,年龄
SELECT name,title,age FROM teacher WHERE age BETWEEN 20 AND 35 AND gender='男'
-
查看3-5楼的班级名称都是什么?
SELECT name FROM class WHERE floor BETWEEN 3 AND 5
DISTINCT去重操作。在结果集中去除指定字段值相同的记录
语法
SELECT DISTINCT 字段1[,字段2,...]
FROM 表名
...
- DISTINCT关键字必须紧跟在SELECT子句之后
- DISTINCT后面可以指定多个字段,当这几个字段组合相同的记录会在结果集中被去除
例
-
查看老师的职称都有哪些?
SELECT title FROM teacher 上述SQL语句的查询结果集是展现每个老师的职称,与查询需求不匹配 SELECT DISTINCT title FROM teacher 将查询结果集中重复的title去除后得到正确效果
-
查看学生的职位都有哪些?
SELECT DISTINCT job FROM student
-
查看各年龄的职位都有哪些?
SELECT DISTINCT age,job FROM student
练习:
1.查看负责课程编号(subject_id)为1的男老师都有谁?
SELECT name,salary,title
FROM teacher
WHERE subject_id=1 AND gender='男'
2.查看工资高于5000的女老师都有谁?
SELECT name,salary,title
FROM teacher
WHERE salary>5000 AND gender='女'
3.查看工资高于5000的男老师或所有女老师的工资?
SELECT name,salary,gender
FROM teacher
WHERE salary>5000 AND gender='男' OR gender='女'
4.查看所有9岁学生的学习委员和语文课代表都是谁?
SELECT name,job,age
FROM student
WHERE age=9 AND (job ='学习委员' OR job='语文课代表')
5.查看工资在6000到10000之间的老师以及具体工资?
SELECT name,salary,title
FROM teacher
WHERE salary BETWEEN 6000 AND 10000
6.查看工资在4000到8000以外的老师及具体工资?
SELECT name,salary,title
FROM teacher
WHERE salary NOT BETWEEN 4000 AND 8000
7.查看老师负责的课程编号都有什么?
SELECT DISTINCT subject_id
FROM teacher
8.查看所有女老师的职称都是什么?
SELECT DISTINCT title
FROM teacher
WHERE gender='女'
9.查看7-10岁的男同学的职位都有哪些?
SELECT DISTINCT job
FROM student
WHERE age BETWEEN 7 AND 10
AND gender='男'
10.查看一级讲师和二级讲师的奖金(comm)是多少?
SELECT name,comm
FROM teacher
WHERE title IN ('一级讲师','二级讲师')
11.查看除老板和总监的其他老师的工资和奖金是多少?
SELECT name,title,salary,comm
FROM teacher
WHERE title NOT IN('老板','总监')
12.查看'3年级2班'和'5年级3班'在那层楼?
SELECT name,floor
FROM class
WHERE name IN('3年级2班','5年级3班')
LIKE 模糊查询
LIKE中有两个通配符:
- _:下划线表示一个字符(1个)
- %:百分号表示任意个字符(0-多个)
组合示意:
%X%:字符串中含有X。
X%:字符串是以X字符开始的。
%X:字符串是以X字符结尾的。
__X:三个字符,且最后一个字符是X的。
%X_:倒数第二个字符是X的。
例
-
查看名字中含有’苍’的老师?
SELECT name,title,salary FROM teacher WHERE name LIKE '%苍%'
-
查看姓’张’的学生都有谁?
SELECT name,job,gender,age FROM student WHERE name LIKE '张%'
-
查看名字是以’晶’结尾的老师都有谁?
SELECT name,title,salary FROM teacher WHERE name LIKE '%晶'
-
查看三个字名字且最后一个字是’平’的学生都有谁?
SELECT name,job,gender,age FROM student WHERE name LIKE '__平'
-
查看三个字名字中第二个字符是’平’的学生都有谁?
SELECT name,job,gender,age FROM student WHERE name LIKE '__平'
-
查看职位是’什么课什么表’的学生都有谁?
SELECT name,job,gender,age FROM student WHERE job LIKE '%课%表'
-
查看所有2班都在哪层?
SELECT name,floor FROM class WHERE name LIKE '%2班'
练习
1.查询名字姓"李"的学生姓名
SELECT name,age
FROM student
WHERE name LIKE '李%'
2.查询名字中包含"江"的学生姓名
SELECT name,age
FROM student
WHERE name LIKE '%江%'
3.查询名字以"郭"结尾的学生姓名
SELECT name,age
FROM student
WHERE name LIKE '%郭'
4.查询9-12岁里是"课代表"的学生信息
SELECT name,age,job
FROM student
WHERE age BETWEEN 9 AND 12
AND job LIKE '%课代表'
5.查询名字第二个字是"苗"的学生信息
SELECT name,age
FROM student
WHERE name LIKE '_苗%'
6.查询姓"邱"的课代表都是谁?
SELECT name,job
FROM student
WHERE name LIKE '邱%'
AND job LIKE '%课代表'
NULL值判断
- 等于NULL:IS NULL
- 不等于NULL:IS NOT NULL
- 不能直接用=或<>判断NULL值
例:
-
查看没有奖金的老师都有谁?(奖金字段值为null的记录)
SELECT name,salary,comm FROM teacher WHERE comm=NULL 上述SQL是查询不到任何记录的,因为comm的值不能用=来判断NULL。 SELECT name,salary,comm FROM teacher WHERE comm IS NULL
-
查看有奖金的老师都有谁?
SELECT name,salary,comm FROM teacher WHERE comm IS NOT NULL
ORDER BY子句
ORDER BY子句用于指定查询的结果集按照指定的字段进行排序
- ORDER BY 字段名 [ASC] 将结果集按照指定的字段值升序排序(从小到大)
- ORDER BY 字段名 DESC 将结果集按照指定的字段值降序排序(从大到小)
- ORDER BY不指定排序方式时,默认为升序
- ORDER BY 可以按照多字段排序:ORDER BY 字段1 ASC|DESC, 字段2… ASC|DESC
- 多字段排序时,首先结果集按照第一个字段值的排序方式排序
- 当第一个字段值相同的记录再按照第二个字段的排序方式继续排序
- 以此类推
- ORDER BY子句只能是DQL语句中的最后一个子句
例:
-
查看老师工资的排名情况
SELECT name,salary,title FROM teacher ORDER BY salary DESC
-
查看老师的奖金情况,按照从少到多排序。
SELECT name,comm FROM teacher ORDER BY comm
-
查看学生的生日从远到近。
日期字段排序是,越早的日期越小,越晚的日期越大。 SELECT name,birth FROM student ORDER BY birth
-
查看7-10岁学生名字,年龄,生日。查看时按照年龄从小到大。
SELECT name,age,birth FROM student ORDER BY age,birth DESC
-
查看老师的奖金和工资,按照奖金的升序,工资的降序
SELECT name,comm,salary FROM teacher ORDER BY comm,salary DESC;
分页查询
当一条DQL语句可以查询出很多行记录时,为了减少资源开销,以及网络间的传送速度,我们通常都会使用分页查询来分批,分段的将DQL查询的结果集获取到。
分页是方言,标准SQL92标准中没有对分页的语法定义。
在MYSQL中分页使用LIMIT实现。在ORACLE中使用ROWNUM实现
语法:
在ORDER BY子句中使用LIMIT N,M
ORDER BY 字段
LIMIT N,M
-
N表示跳过结果集的记录数
-
M表示从跳过N条记录后开始连续显示M条记录
-
分页上两个条件:1:页数 2:每页显示的条数
-
N的公式:(页数-1)*每页显示的条数
-
M:每页显示的条数
-
例如:一页显示15条,显示第3页?
LIMIT (3-1)*15,15
-
例:
-
查看老师工资排名的前5名是谁?
SELECT name,salary FROM teacher ORDER BY salary DESC LIMIT 0,5
-
按照老师奖金的降序排序后,每页显示3条,显示第5页
- N的公式:(页数-1)*每页显示的条数 - M:每页显示的条数 SELECT name,comm FROM teacher ORDER BY comm DESC LIMIT 12,3
DQL中可以使用函数或者表达式进行查询
在SELECT子句中使用函数或表达式
使用表达式
例:
-
查看每个老师的年薪是多少?
SELECT name,salary,salary*12 FROM teacher
使用函数
在计算表达式中,任何数字与NULL运算结果都是NULL
IFNULL(arg1,arg2)函数
-
当arg1不为NULL时,函数返回arg1的值
-
当arg1为NULL时,函数返回arg2的值
-
IFNULL作用时将一个NULL值换成一个非NULL值
-
内部逻辑
IFNULL(arg1,arg2){ if(arg1!=null){ return arg1; }else{ return arg2 } }
例:
-
查看每个老师的工资+奖金是多少?
SELECT name,salary,comm,salary+comm FROM teacher 上述SQL对于salary+comm而言,若comm字段为NULL时计算结果也为NULL SELECT name,salary,comm,IFNULL(comm,0) FROM teacher 上述SQL会将comm字段为null的记录将该字段改为0 查看老师工资+奖金的实际写法: SELECT name,salary,comm,salary+IFNULL(comm,0) FROM teacher
在WHERE子句中使用函数或表达式作为过滤条件
使用表达式
例
-
查看年薪小于6万的老师都有谁?
SELECT name,salary,salary*12 FROM teacher WHERE salary*12<60000 查询时只有该条记录salary字段值*12小于60000的会被查出
使用函数
例:
-
查看奖金小于3000的老师都有谁?
比较运算时也会忽略NULL值的
SELECT name,comm FROM teacher WHERE comm<3000 上述查不出奖金字段为NULL的记录。 SELECT name,comm FROM teacher WHERE IFNULL(comm,0)<3000
别名
-
别名可以在SELECT子句中为函数,表达式取别名,是的结果集中该字段可读性更强。
-
别名也可被用在其他子句上,比如在FROM子句中为表取别名,便于关联查询。
当SELECT子句中含有函数或表达式时,通常都会指定别名
语法
-
字段名 别名
SELECT salary*12 salary
-
字段名 AS 别名
SELECT salary*12 AS salary
-
字段名 [AS] ‘字段名’
SELECT salary*12 AS 'salary' 或 SELECT salary*12 'salary'
-
字段名 [AS] “字段名”
SELECT salary*12 AS "salary" 或 SELECT salary*12 "salary"
-
当别名中含有SQL关键字或空格时要加引号
SELECT name,salary*12 from 由于from是关键字,SQL语句语法错误(两次FROM) FROM teacher 正确写法 SELECT name,salary*12 'from' FROM teacher 下述SQL本意为salary*12取别名"annu sal" SELECT name,salary*12 annu sal 数据库理解annu为别名,再空格sal认为语法错误 FROM teacher 正确写法 SELECT name,salary*12 'annu sal' FROM teacher
例:
-
查看每个老师的年薪是多少?
SELECT name,salary*12 salary FROM teacher
练习
1.查询所有10岁学生的生日,按生日对应的年纪从大到小.
SELECT name,age,birth
FROM student
WHERE age=10
ORDER BY birth
2.查询8岁同学中名字含有"苗"的学生信息
SELECT name,age
FROM student
WHERE age=8
AND name LIKE '%苗%'
3.查询负责课程编号1和2号且工资高于6000的老师信息
SELECT name,salary,subject_id
FROM teacher
WHERE salary>6000
AND subject_id IN (1,2)
4.查询10岁以上的语文课代表和数学课代表
SELECT name,age,job
FROM student
WHERE age>10
AND job IN ('语文课代表','数学课代表')
5.查询不教课程编号1的老师信息,按照工资降序排序
SELECT name,salary,subject_id
FROM teacher
WHERE subject_id<>1
ORDER BY salary DESC
6.查询没有奖金的老师信息
SELECT name,comm
FROM teacher
WHERE IFNULL(comm,0)=0
7.查询所有老师的奖金,并按照奖金降序排序
SELECT name,comm
FROM teacher
ORDER BY comm DESC
8.查看工资高于8000的老师负责的课程编号都有那些?
SELECT DISTINCT subject_id
FROM teacher
WHERE salary>8000
9.查看全校年龄最小学生的第6-10名
SELECT name,age,birth
FROM student
ORDER BY birth DESC
LIMIT 5,5
聚合函数
聚合函数(也称为多行函数,分组函数)是用来对结果集按照指定字段统计的。
聚合函数可以将多行记录中的指定字段统计出一条结果。
聚合函数:
- MIN:统计结果集中指定字段值最小的
- MAX:统计结果集中指定字段最大的
- AVG:统计结果集中指定字段的平均值
- SUM:统计结果集中指定字段的总和
- COUNT:统计结果集中指定字段非空值共多少行
- 聚合函数忽略NULL值的统计,在AVG和COUNT中最为明显
MIN,MAX,AVG,SUM是对字段值的统计。COUNT是对记录数的统计。
使用聚合函数时,通常要先将参与统计的记录查询出来后再决定添加何种聚合函数进行统计。
例
-
查看所有老师的平均工资是多少?
1:准备参与统计的记录对应的SQL语句 下述SQL用来查询出所有老师的工资: SELECT salary FROM teacher 2:为salary字段添加聚合函数,求所有记录salary的平均值 SELECT AVG(salary) FROM teacher
-
查看所有老师的最高工资,最低工资,工资总和和平均工资分别是多少?
SELECT MAX(salary),MIN(salary),SUM(salary),AVG(salary) FROM teacher
-
查看负责课程编号1的老师的平均工资是多少?
1:查询出负责课程编号1的老师工资分别是多少? SELECT salary FROM teacher WHERE subject_id=1 2:在上述SQL中对salary字段添加聚合函数求平均值 SELECT AVG(salary) FROM teacher WHERE subject_id=1
-
查看共有多少位老师?
COUNT(*)作为统计被所有数据库都进行了优化,因此统计记录数时就用它
SELECT COUNT(name) FROM teacher 上述SQL统计结果共20条。teacher表中每条记录的name都有值 SELECT COUNT(comm) FROM teacher 上述SQL统计结果共17条。原因:teacher表中有3条记录comm字段值为NULL 聚合函数是忽略NULL值的 SELECT COUNT(*) FROM teacher
-
查看所有老师的平均奖金
SELECT AVG(comm),SUM(comm) FROM teacher 上述SQL是对comm字段有的所有记录取平均值 将NULL值替换为非NULL值再进行统计 SELECT AVG(IFNULL(comm,0)) FROM teacher
-
查看负责课程编号1的老师共几人?
SELECT COUNT(*) FROM teacher WHERE subject_id=1
-
查看班级编号(class_id)为1的学生有多少人?
SELECT COUNT(*) FROM student WHERE class_id=1
-
查看全校学生生日最大的是哪天?
SELECT MIN(birth) FROM student
-
查看11岁的课代表总共多少人?
SELECT COUNT(*) FROM student WHERE age=11 AND job LIKE '%课代表'
GROUP BY子句,分组
GROUP BY子句在DQL语句中是对查询结果集按照指定的字段值相同的记录进行分组,配合聚合函数进行组内统计。
若DQL的SELECT子句中没有聚合函数时,不会使用GROUP BY子句。
在SELECT子句中不在聚合函数中的字段都必须出现在GROUP BY子句中
按照单字段分组
例
-
教每门课程的老师的平均工资是多少?
SELECT AVG(salary),subject_id FROM teacher GROUP BY subject_id GROUP BY子句的作用是将: SELECT salary,subject_id FROM teacher 查询结果集按照subject_id字段值相同的记录分组,在组内统计结果。
-
查看每个班级各多少学生?(将student表中所有记录按照class_id字段值相同的记录分组,组内统计记录数)
SELECT COUNG(*),class_id FROM student GROUP BY class_id
-
查看学校每种职位的学生各多少人?以及最大生日和最小生日
SELECT COUNT(*) '人数',MIN(birth) '最大生日',MAX(birth) '最小生日',job FROM student GROUP BY job
按照多字段分组
GROUP BY子句若指定了多个字段,那么只有当这几个字段值都相同的记录才会被划分为一组。
例
-
查看每个班每种职位各多少人?
SELECT COUNT(*),job,class_id FROM student GROUP BY job,class_id
按照聚合函数排序
在SELECT子句中为聚合函数取别名,这样方便在ORDER BY子句中排序
例
-
查看每个科目老师的平均工资,并按照平均工资从少到多排序
SELECT AVG(salary),subject_id FROM teacher GROUP BY subject_id ORDER BY AVG(salary) 建议为聚合函数取别名,并按照该别名排序 SELECT AVG(salary) avg,subject_id FROM teacher GROUP BY subject_id ORDER BY avg
HAVING子句,在分组统计时添加过滤条件
例
-
仅查看平均工资高于6000的那些课程的老师平均工资
SELECT AVG(salary),subject_id FROM teacher WHERE AVG(salary)>6000 GROUP BY subject_id
执行后报错:
聚合函数不能被应用与WHERE子句
原因:过滤实际不同
-
WHERE条件实在检索表中数据时,每检索一行记录就会进行一次WHERE,来决定该条记录是否被查询出来
-
聚合函数想作为过滤条件(将统计结果作为过滤条件),统计的前提时要先查询出结果集,而查询结果集是WHERE的实际。因此根据统计结果再进行过滤应当已经是WHERE之后进行的了。
HAVING子句
HAVING子句仅跟在GROUP BY之后。因此若没有GROUP BY子句就不会写HAVING子句。
HAVING子句也是用于添加过滤条件的,过滤时机:分组统计出结果后进行二次过滤的。
HAVING子句是对分组过滤的:只有满足HAVING子句条件的分组才会进行SELECT中相应的统计。
WHERE子句是对记录过滤的:只有满足WHERE子句条件的记录才会进行SELECT中相应的查询
WHERE先执行,用于确定查询出的结果集。HAVING后执行用于确定保留结果集中的那些分组
例
-
仅查看平均工资高于6000的那些课程的老师平均工资
子句执行顺序 SELECT AVG(salary),subject_id 4:最后查询对应的内容(仅查询满足HAVING要求的组) FROM teacher 1:确定数据来自哪里 GROUP BY subject_id 2:确定数据按照哪个字段分组(按照subject_id分6组) HAVING AVG(salary)>6000 3:确定仅保留哪些分组(仅有3组符合AVG要求)
-
查看每个科目老师的平均工资,前提是该科老师最高工资高于9000.
SELECT AVG(salary),subject_id 4:最后将过滤出的2组进行统计结果 FROM teacher 1:确定数据来自teacher表 GROUP BY subject_id 2:按照subject_id字段值相同的可被分出6组 HAVING MAX(salary)>9000 3:分出的6组都要看最高工资是否>9000,最终过滤出2组。
-
查看科目老师的工资总和是多少?前提是该科老师的平均奖金要高于4000.
SELECT SUM(salary),subject_id FROM teacher GROUP BY subject_id HAVING AVG(IFNULL(comm,0))>4000
子查询
嵌套在其他SQL语句中的一条DQL语句就被称为子查询。
子查询使用场景
- DQL中:可以基于一个查询结果集再进行查询(先执行的这条DQL就是子查询,也是子查询最常用的场景)
- DML中:基于一个查询结果集进行增删改操作
- DDL中:基于一个查询结果集进行数据库对象的操作(创建表,创建视图)
子查询分类
- 单行单列子查询:查询结果集中只有一个值
- 多行单列子查询:查询结果集中有多个值
- 多行多列子查询:查询结果集就是一张表
在DQL语句中使用子查询
在DQL中子查询部分要被"()"括起来使用
例:
-
查看哪个老师的工资高于王克晶的工资?
-
查询王克晶的工资是多少?
SELECT salary FROM teacher WHERE name='王克晶' 通过上述SQL我们可以得到一个单行单列的查询结果集:salary===>8000
-
查看谁的工资高于8000(王克晶的工资)?
SELECT name,salary FROM teacher WHERE salary>8000
-
-
将步骤1的SQL替换为步骤2的8000
示意: int money = SELECT salary FROM teacher WHERE name='王克晶' 不是真实语法!!! 查询示意: SELECT name,salary FROM teacher WHERE salary>money 真实写法: SELECT name,salary FROM teacher WHERE salary>(SELECT salary FROM teacher WHERE name='王克晶')
-
查看那些高于老师平均工资的老师工资都是多少?
1:查看老师的平均工资是多少? SELECT AVG(salary) FROM teacher 2:谁的工资高于平均工资? SELECT name,salary FROM teacher WHERE salary>(SELECT AVG(salary) FROM teacher)
-
查看和’李费水’在同一个班的学生都有谁?
1:查看‘李费水’所在班级的班级号 SELECT class_id FROM student WHERE name='李费水' 2:查看和'李费水'在同一个班的学生都有谁? SELECT name,class_id FROM student WHERE class_id=(SELECT class_id FROM student WHERE name='李费水')
-
查看工资最高的老师的工资和奖金是多少?
1:最高工资是多少? SELECT MAX(salary) FROM teacher 2:查看工资最高的老师的工资和奖金是多少? SELECT name,salary,comm FROM teacher WHERE salary=(SELECT MAX(salary) FROM teacher)
** 在单行单列子查询中,若我们使用其作为过滤条件使用时,可以搭配:=, >, >=, <, <=, <>使用**
** 在多行单列子查询中(查询结果集有多个值),在过滤条件中要搭配:IN,ANY,ALL**
-
ANY与ALL是与:>, >=, <, <=联合使用的
- >ANY(列表):大于列表其中之一(大于最小的即可)
- <ANY(列表):小于列表其中之一(小于最大的即可)
- >ALL(列表):大于列表所有(大于最大的)
- <ALL(列表):小于列表所有(小于最小的)
-
IN(列表)或NOT IN(列表):在列表中或不在列表中
-
上述所说的列表指的是一个多行单列子查询的结果集
例
-
查看与"祝雷"和"李费水"在同一个班的学生都有谁?
1:查看祝雷和李费水的班级号? SELECT class_id FROM student WHERE name IN('祝雷','李费水') 2:查看与"祝雷"和"李费水"在同一个班的学生都有谁? SELECT name,class_id FROM student WHERE class_id=(SELECT class_id FROM student WHERE name IN('祝雷','李费水')) 上述SQL执行后会报错,因为子查询返回了多个值,"="不能同时等于多个值
正确写法:
SELECT name,class_id
FROM student
WHERE class_id IN(SELECT class_id
FROM student
WHERE name IN('祝雷','李费水'))
-
查看比教科目2和科目4老师工资都高的老师都有谁?
1:查看科目2和科目4老师的工资都是多少? SELECT salary FROM teacher WHERE subject_id IN(2,4) 2:查看比教科目2和科目4老师工资都高的老师都有谁? SELECT name,salary,subject_id FROM teacher WHERE salary>ALL(SELECT salary FROM teacher WHERE subject_id IN(2,4))
在DML中使用子查询
例:
-
将负责与"范传奇"教同一科目的所有老师工资涨500
1:查看范传奇负责的科目号? SELECT subject_id FROM teacher WHERE name='范传奇' 2:更新数据 UPDATE teacher SET salary=salary+500 WHERE subject_id=(SELECT subject_id FROM teacher WHERE name='范传奇')
在DDL中使用子查询
** 在创建表时,可以将一个子查询的结果集当作表创建出来。后期使用的视图也是如此。**
例:
-
创建一张表用于记录老师工资的统计情况(按照科目)。要记录每门课老师的最高工资,最低工资,平均工资和工资总和以及科目编号。表名:teacher_salary_info
1:创建表 CREATE TABLE teacher_salary_info( max_salary INT, min_salary INT, avg_salary INT, sum_salary INT, subject_id INT ) 2:先执行DQL查询对应结果,在使用DML中的INSERT语句逐行插入。 若数据太多,工作量太大且笨重。 SELECT MAX(salary),MIN(salary),AVG(salary),SUM(salary),subject_id FROM teacher GROUP BY subject_id 实际写法: 注意:如果子查询的SELECT子句中的字段是函数或表达式则必须要指定别名(会作为新创建的表的字段名) CREATE TABLE teacher_salary_info AS SELECT MAX(salary) max_sal,MIN(salary) min_sal, AVG(salary) avg_sal,SUM(salary) sum_sal, subject_id FROM teacher GROUP BY subject_id
关联查询(重点知识)
结果集中的字段来自于多张表。联合多张表进行查询。
表与表之间的数据存在着一种对应关系,这种对应关系被称为表与表之间的关联关系。
表之间的关联关系分为三种:
一对一:A表的一条记录仅对应B表的一条记录。反之亦然。
一对多:A表的一条记录对应B表的多条记录,反之B表的一条记录仅对应A表的一条记录。A与B是一对多的。
多对多:当两张表双向都是一对多时就是多对多关系。
在关联查询中必须要指定连接条件,连接条件作用是让数据库直到A表的记录与B表的哪些记录对应
关联查询中不指定连接条件会产生笛卡尔积,笛卡尔积通常是一个无意义的结果集(少数特殊业务除外)
多张表进行关联查询时,要满足当N张表联合查询时要指定最少N-1个连接条件。
- 例如:3张表关联查询就至少要有2个连接条件。5张表关联查询就至少要有4个连接条件。
笛卡尔积
缺少连接条件产生笛卡尔积
特点:
以两张表A和B关联查询,结果集中包含的数据A表每条记录与B表每条记录产生的记录数。记录总数应当时A表记录数*B表记录数。
关联查询语法:
SELECT 字段...
FROM 表A,表B,...
WHERE 连接条件
AND 过滤条件
注意:如果在关联查询中含有过滤条件时必须与连接条件同时满足,否则会产生笛卡尔积
例
-
查看类名老师的名字和其任课的科目名称?
SELECT t.name,s.name FROM teacher t,subject s WHERE t.subject_id=s.id 实际查询时,数据库会从teacher表中将其中一条记录与subject表中的每条记录都关联一次,但是仅将符合连接条件的记录分别取出查询的字段构成结果集中的一条记录。
- 不指定连接条件时,可以通过结果集看出teacher表的每条记录都与subject表每条记录关联了一次产生一条结果(产生了笛卡尔积,记录数是teacher表的20条与subject表的5条的乘积,共100条记录!!!)
-
查看班级的名称和对应的班主任(老师)是谁?
1:数据需要来自哪些表? 班级名称来自class表。老师名字来自teacher表。 明确FROM子句内容:FROM class c,teacher t 2:明确class表与teacher表记录之间的对应关系(关联关系) class表记录中teacher_id字段的值与teacher表记录中id字段的值相同的记录产生对应关系。 明确WHERE子句连接条件:WHERE c.teacher_id=t.id SELECT c.name,t.name FROM class c,teacher t WHERE c.teacher_id=t.id
-
查看每个学生的名字,年龄,以及其所在的班级名称和所在楼层
1:确定数据来自哪些表? 学生信息来自student表。班级信息来自class表 明确FROM子句:FROM student s,class c 2:student表与class表记录的对应关系? 学生表某条记录的class_id值与class表某记录的id值相同的产生对应关系 明确WHERE子句连接条件:s.class_id=c.id SELECT s.name,s.age,c.name,c.floor FROM student s,class c WHERE s.class_id=c.id
关联查询中可以指定过滤条件
过滤条件要与连接条件同时满足
例
-
"王克晶"是哪个班的班主任?查看班级名称,所在楼层和班主任名字以及工资
SELECT c.name,c.floor,t.name,t.salary FROM class c,teacher t WHERE c.teacher_id=t.id AND t.name='王克晶'
-
查看三年级的班级班主任都是谁?要列出班级名称,所在楼层,班主任名字和工资
SELECT c.name,c.floor,t.name,t.salary FROM class c,teacher t WHERE c.teacher_id=t.id AND c.name LIKE '3年级%'
-
查看5年级的中队长都有谁?要列出学生名字,年龄,性别,职位和所在班级的名字以及楼层
SELECT s.name,s.age,s.gender,s.job,c.name,c.floor FROM student s,class c WHERE s.class_id=c.id 连接条件 AND c.name LIKE '5年级%' 过滤条件 AND s.job='中队长' 过滤条件
N张表关联查询至少要有N-1个连接条件
例
-
查看"范传奇"所带班级的学生都有谁?要列出:学生名字,年龄,班级名称,老师名字
1:查询的数据来自哪些表 学生信息来自student表。班级信息来自class表。老师信息来自teacher表 明确FROM子句:FROM student s,class c,teacher t 2:明确连接条件 3张表关联至少要有2个连接条件 2.1:学生表与班级表中的记录对应关系 s.class_id=c.id 2.2:班级表与老师表中的记录对应关系 c.teacher_id=t.id 3:过滤条件来自哪张表? 老师名字叫"范传奇",来自teacher表 SELECT s.name,s.age,c.name,t.name FROM student s,class c,teacher t WHERE s.class_id=c.id 连接条件 AND c.teacher_id=t.id 连接条件 注:所有的连接条件要同时满足 AND t.name='范传奇' 过滤条件
-
查看1年级1班的同学的名字和来自的城市
1:数据来自那些表 城市信息来自location表。学生信息来自student表 2:过滤条件来自哪些表? 查看1年级1班的。条件来自class表。 确定WHERE子句的过滤条件:c.name='1年级1班' 上述两条联合确定 FROM子句:FROM location l,student s,class c 3:连接条件 3.1:班级与学生的连接条件:c.id=s.class_id 3.2:学生与城市的连接条件:s.location_id=l.id WHERE子句连接条件:WHERE c.id=s.class_id AND s.location_id=l.id SELECT s.name,l.name FROM location l,student s,class c WHERE c.id=s.class_id AND s.location_id=l.id 连接条件 AND c.name='1年级1班' 过滤条件
练习:
1.查看来自北京的学生都是谁?
2.教"英语"的老师都是谁?
3.刘苍松所带班级的学生都有谁?
4.教语文的老师所带的班级有哪些?
5.王克晶所带的班级学生都来自哪些城市(去重)?
6.3年级的几个班主任都教哪些课程?
7.工资高于10000的老师所带班里的大队长都是谁?
8."李费水"的班主任教哪门课?
9.所在4楼的班里的大队长和中队长以及班主任都是谁?
10.全校最小的同学的班主任是谁?
HAVING和WHERE的区别
HAVEING : 后执行,过滤分组,仅在GROUP BY后
WHERE : 先执行, 过滤记录