常见SQL题

本文通过多个实例,详细介绍了如何使用SQL解决实际问题,包括查询特定条件的数据、计算平均值、求解排名等,适用于初学者及有一定基础的读者。

1.在a表不在b表

有2张表s1和s2,每张表都只有一列(列名:id),现在想抽取仅存在于t1的id

思路:
使用左连接,左边会全部显示,而右边关联不上的则会为空,以此来判断仅在s1表中存在。

注意:
1)使用左连接的时候,当右边没有匹配值的时候会自动赋NULL
2)在判断是否为NULL的时候用IS,而不是用=
3)这里模拟的是表中不止有一列,所以需要用临时表。如果只有一列就没必要用临时表。所以在写的过程中尽量少的使用临时表。

答:

SELECT s1.id
FROM(
	SELECT id FROM school.student
) s1
LEFT OUTER JOIN
(
	SELECT id FROM school.student1
) s2
on s1.id = s2.id
WHERE s2.id IS NULL;

2. 求平均成绩大于60的学生

注意:
1)where,group by,having,order by是有顺序的,这个顺序不能更改
2) 使用嵌套语句的时候,外层要指定查询的是哪两个表中的数据

方法一:使用嵌套语句

SELECT s.name, s.avg_socre
FROM (
	SELECT name,AVG(score) avg_socre
	FROM school.student
	GROUP BY name 
) s
WHERE s.avg_socre> 60;

方法二:使用having

having可以看做分组筛选

SELECT name, AVG(score) avg_socre
FROM school.student 
GROUP BY name
HAVING avg_socre > 60

错误的使用:
这就是初学者容易犯的错误,觉得分组之后直接用where就可以进行筛选,其实这是不行的

SELECT name, AVG(score) avg_socre
FROM school.student 
GROUP BY name
WHERE avg_socre > 60

3. 分组求和TOPN

即求每个学科的前N名

(其实这是一道HIVESQL题,如果使用HiveSQL可以很简单的写出来)

解法1: 暴力解法,求每科的前N个,然后进行uoion all

(SELECT * FROM student WHERE course = '计算机网络' ORDER BY score DESC LIMIT 3)
UNION all 
(SELECT * FROM student  WHERE course = '数据结构' ORDER BY score DESC LIMIT 3)
·······

解法2:

使用a表和b表,查询a表中某一行成绩在b表中大于该行成绩(相同课程)的个数。如果小于3,则代表该行成绩属于前三。

SELECT *
FROM student a 
WHERE (
	SELECT COUNT(*)
	FROM student b
	WHERE a.course = b.course AND a.score < b.score 
) < 3
ORDER BY a.course, a.score DESC;

解法3:HiveSQL

正在学习hive,还没有学到这里,先做记录,后面再补

select  * from (
    select *, ROW_NUMBER() over(partition by course order by score desc) rowNum
    from scores 
) as s where s.rowNum<=3

4.分组求及格率

表t1有3列:id, sex, score,3列均为int型,id表示员工工号,
gender表示性别(0女1男),score代表员工某次考试的成绩(0-100之间),
若成绩大于等于60为及格,请使用sql语句查询不同性别员工的及格率

解法1:

  1. 使用两个表:
    表1:查每个性别及格的人数
    表2:查每个性别的总人数
  2. 联表查询,查及格率
SELECT s1.sex, s1.count1/s2.count2 res
FROM 
(
	SELECT sex,COUNT(*) count1
	FROM student 
	WHERE score > 60
	GROUP BY sex
) s1
LEFT OUTER JOIN 
(
	SELECT sex,COUNT(*) count2
	FROM student 
	GROUP BY sex
) s2
ON s1.sex = s2.sex

解法2:

使用sum (case when),当分数大于60就加1,否则加0;最后除以总数。使用group by分组 。

PS:讲实话,我真觉得面试现场想,想不出来这种写法。

select sex,
sum(case when score>=60 then 1 else 0 end) / count(*)
FROM student 
group by sex

5.GROUP BY求最高分

例如下表:成绩表,求每位学生多门课程中的最高分(返回name,course,score)
在这里插入图片描述
这是一道经典的MYSQL GROUP BY 的面试题,为什么这么说呢?因为往往会有人这么写:

SELECT name, course, MAX(score)
FROM student
GROUP BY name;

往往觉得使用MAX求最大分数,再按照名字group by就好了。实际上这样写是报错的,因为GROUP BY的使用是有规范的,查询的字段要不然在聚合函数里,要不然就在group by指定的字段中。不能出现上述的这种情况,course在查询字段里,但是不在聚合函数中也不在group by的字段中。

但是如果这么写就是可以的,但是不符合要求:查询字段不够

SELECT name, MAX(score)
FROM student
GROUP BY name;

正确写法:

解法一:相关子查询

s2表求分数的最大值,s1进行name匹配。

这里要注意的是,s2的确没有查询name字段,但是不代表没有name字段

SELECT name, course, score
FROM student s1 
WHERE s1.score = 
(
SELECT MAX(score)
FROM student s2
WHERE s1.name = s2.name 
)

解法二:非相关子查询

s1表和s2表进行左连接,对s1表中的每一行都去s2中找相同名字的,比他大的,如果找不到就代表s1这里是最大的。

SELECT s1.name, s1.course, s1.score
FROM student s1
LEFT OUTER JOIN
student s2
ON s1.name = s2.name AND s1.score < s2.score 
WHERE s2.name IS NULL 

6. 求平均收入大于某值的公司

一张收入表:公司id,收入profit
要求:平均收入大于1000的公司

注意不可以使用where直接对平均收入进行筛选,where后面不能跟聚合函数。后面会讲案例

**解法一:**使用AVG函数求平均收入,使用group by分组,使用having进行分组筛选

SELECT id, AVG(profit) as avg_profit
FROM company_profit
GROUP BY id
HAVING avg_profit > 1000

只查id的情况

SELECT id
FROM company_profit
GROUP BY id
HAVING AVG(profit) > 1000

错误写法:经典错误,很容易犯

之前刚学习了where必须写在group by前面,where的执行顺序也在group by前面,因此where后面不能跟聚合函数。要想进行分组筛选,就使用having或者嵌套select语句。

SELECT id, AVG(profit) as avg_profit
FROM company_profit
WHERE avg_profit > 1000
GROUP BY id

7. 学生成绩表+课程表经典题

成绩表:
在这里插入图片描述
课程表:
在这里插入图片描述
1)如果学号的前两位表示年纪,要查找19级男生的姓名,请写出相应SQL语句。

解法一:like模糊查询,因为这不是学生表而是成绩表,因此学号会重复,要使用DISTINCT去重。

SELECT DISTINCT(sno), name, sex
FROM score_table
WHERE sno LIKE "19%" AND sex = 1

解法二:左匹配

SELECT DISTINCT(sno), name, sex
FROM score_table
WHERE LEFT(sno,2) = '19' AND sex = 1

2)要查找所有需要补考(小于60分)的学生姓名和课程的名字和成绩,请写出相应SQL语句。

很简单的关联查询

SELECT s.sno,s.name,c.co_name, s.score
FROM course_table c, score_table s
WHERE s.courseid = c.id AND s.score < 60

3)找出所有学生都不及格的课程及其平均分

先找出及格的课程,然后not in找出不及格的课程,然后AVG聚合函数求平均分,使用GROUP BY分组

SELECT a.courseid, b.co_name ,AVG(score)
FROM score_table a, course_table b
WHERE a.courseid NOT IN 
(
SELECT DISTINCT(courseid)
FROM score_table
WHERE score > 60
) AND a.courseid = b.id
GROUP BY a.courseid

4)找出所有课程分数都在80以上的学生

思路:找出分数在80一下的学生,用not in即可

SELECT sno, name
FROM score_table 
WHERE sno NOT IN
(
SELECT sno
FROM score_table
WHERE score < 80
)

8. 求等级排名

题目:求身高排序
在这里插入图片描述
题解:

先用a和b做笛卡尔积,然后求出b中大于等于a成绩的个数,这个数量就是a表中的排名

SELECT a.name, count(DISTINCT(b.high)) rank
FROM hight_table a, hight_table b
WHERE a.high <= b.high
GROUP BY name
ORDER BY rank
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值