sql题目
第一题

第一题:
用一条sql语句,查询出每门课程都大于80分的学生姓名
建表语句:
1 | drop table if exists t_student; |
思路:查询出小于80分的所欲学生姓名(去重),然后使用not in,不去重会有多个少于80分的重复名字
1 | # 第一步:找小于等于80分的学员姓名 |

第二题


其中,两个表的关联字段为申请单号。
1)查询身份证号为440401430103082的申请日期。
2)查询同一个身份证号码有两条以上记录的身份证号码及记录个数。
3)将身份证号码为440401430103082的记录在两个表中的申请状态均改为07。
4)删除g_cardapplydetail表中所有姓李的记录。
模拟数据:考试做这种题目最重要的是要冷静下来,只有静下来SQL才能写好。要模拟数据。看到数据SQL就好写了。

1 | drop table if exists g_cardapply; |
1)查询身份证号为440401430103082的申请日期。
bigint转date,可以使用from_unixtime函数。
1 | select a.g_applydate from g_cardapply a join g_cardapplydetail b on a.g_applyno = b.g_applyno where b.g_idcard = '440401430103082' |
2)查询同一个身份证号码有两条以上记录的身份证号码及记录个数。
1 | select count(g_idcard),g_idcard from g_cardapplydetail group by g_idcard having count(g_idcard) >= 2 |
3)将身份证号码为440401430103082的记录在两个表中的申请状态均改为07。
1 | UPDATE |
4)删除g_cardapplydetail表中所有姓李的记录。
要注意a表符合姓李的相关日期记录也需要删除
1 | delete t1,t2 from g_cardapply t1 join g_cardapplydetail t2 on t1.g_applyno=t2.g_applyno where t2.g_name like '李%'; |

第三题


1 | drop table if exists stuscore; |
表名:stuscore
1)统计如下:课程不及格[059]的多少个,良[6080]多少个,优[81-100]多少个。
多种方式:可以采用union实现,也可以采用case when then when then else and实现
case把符合的都转换成了字符串,后续可以再用group by进行分组
then是加上条件,as是起名字,count来统计条数,然后再分组
1 | select case |
2)计算科科及格的人的平均成绩。
1 |
|

第四题

1 | drop table if exists WCMEmploy; |
1)请用一条SQL语句查询出不同部门中担任“钳工”的职工平均工资。
先筛选钳工,再分组,分组可以用dname和job的联合分组
1 | select dname,job,avg(sal) from WCMEmploy where job='钳工' group by dname,job; |
2)请用一条SQL语句查询出不同部门中担任“钳工”的职工平均工资高于2000的部门。
使用having可以对分组结果再过滤
1 | select dname,job,avg(sal) from WCMEmploy where job='钳工' group by dname,job having avg(sal)>2000; |

第五题

Employee是雇员信息表:
雇员姓名(主键):person-name
街道:street
城市:city
Company是公司信息表:
公司名称(主键):company-name
城市:city
Works是雇员工作信息表:
雇员姓名(主键):person-name
公司名称:company-name
年薪:salary
Manages是雇员工作关系表:
雇员姓名(主键):person-name
经理姓名:manager-name
模拟数据:
员工表:employee

公司表:company

雇员工作信息表:Works

雇员工作关系表:Manages

1 | drop table if exists Employee; |
请给出下面每一个查询的SQL语句:
找出所有居住地与工作的公司在同一城市的员工的姓名。
employee e
company c
works w
将以上三张表进行连接,连接条件:
e join w on e.person-name=w.person-name员工表和工作表连接
join c on w.company-name=c.company-name
select可以先不写 ,用where进行过滤1
2
3
4
5select e.`person-name`
from employee e
join works w on e.`person-name`=w.`person-name`
join company c on w.`company-name`=c.`company-name`
where e.city=c.city;找出比Small Bank Corporation的所有员工收入都高的所有员工的姓名。
1
select `person-name` from works where salary>(select max(salary) as maxsal from works where `company-name`='Small Bank Corporation');
找出平均年薪在10000美元以上的公司及其平均年薪。
思路:按照公司进行分组,对年薪求平均值
1
select avg(salary) avgsal,`company-name` from works group by `company-name` having avgsal>10000;

第六题

客户表Client

订单表Order

客户订单表ClientOrder

图书表Book

1 | drop table if exists Client; |
请写出一条SQL语句,查询出每个客户的所有订单并按照地址排序,要求输出格式为:address client_name phone order_id
分成client c
clientorder co
c.client_id=co.client_id1
select c.address,c.client_name,c.phone,co.order_id from client c join clientorder co on c.client_id=co.client_id order by c.address;
请写出一条SQL语句,查询出每个客户订购的图书总价。要求输出格式为:client_name total_price
1
2
3
4
5select c.client_name,sum(b.price) as total_price
from client c join clientorder co on c.client_id=co.client_id
join `order` o on co.order_id=o.order_id
join book b on o.book_id=b.book_id
group by c.client_name;如果要求每个订单可以包含多种图书,应该如何修改Order表的主键?为了保证每个订单只被一个客户拥有,应该在ClientOrder表上增加怎样的约束?
1
2#把order表的order_id和book_id做复合主键
#加上唯一性约束:unique

第七题


模拟数据:
学生表:student

课程表:course

成绩表:sc

教师表:teacher

1 | drop table if exists student; |
查询1号课比2号课成绩高的所有学生学号。
主要是c#,查询出1号和2号课的学生后,要确定两表的s#是一样的,才是同一个人,然后去对比
select * from sc wherec#=1;
select * from sc wherec#=2;1
2
3
4select a.`s#`
from (select * from sc where `c#`=1) a
join (select * from sc where `c#`=2) b
on a.`s#`=b.`s#` where a.score>b.score;查询平均成绩大于60分的学号和平均成绩。
1
select `s#`,avg(score) from sc group by `s#` having avg(score) > 60;
查询所有学生学号、姓名、选课数、总成绩。
求选课数就是count,总成绩sum,联合分组后才可以一起显示学号姓名
1
2select s.`s#`,s.sname,count(sc.`c#`),sum(sc.score)
from student s join sc on s.`s#`=sc.`s#` group by s.`s#`,s.sname;查询姓“李”的老师的个数。
count(tname)记录不为null的个数
1
select count(tname) from teacher where tname like '李%';
查询没学过“叶平”老师课的学号、姓名。
select
t#from teacher where tname=’叶平’这是叶平老师的编号
selectc#from course wheret#=(上面)这是查叶平老师的课的编号
select distincts#from sc wherec#in(上面),c#是查询的字段,in就是要查询的内容1
select * from student where `s#` not in(select distinct `s#` from sc where `c#` in(select `c#` from course where `t#`=(select `t#` from teacher where tname='叶平')));

第八题

学生表:student

课程表:class

选课表:chosen_class

1 |
没有选修课程编号为C1的学生姓名
select s_id from chosen_class where c_id=’c1’;选择了的
select s_id from chosen_class where s_id not in(没选择的
1 | select sname from student where s_id in(select s_id from chosen_class where s_id not in(select s_id from chosen_class where c_id='c1')); |
列出每门课程名称和平均成绩,并按照成绩排序
class c
chosen_class cc
c.c_id=cc.c_id1
select c.c_name,avg(cc.grade) as avgscore from `class` c join chosen_class cc on c.c_id=cc.c_id group by c.c_name order by avgscore;
选了2门课以上的学生姓名。
思路:按照学生姓名分组,计数
student s
chosen_class cc
条件:s.s_id=cc.s_id1
select s.sname,count(c_id) from student s join chosen_class cc on s.s_id=cc.s_id group by s.sname having count(c_id) > 2;

第九题


要转换成:

1 | /* |
MySQL行转列
MySQL行转列又叫做数据透视。什么叫做行转列?将原本横向排列的数据透视成纵向排列的数据,进而进行计算、分析、展示等操作。
假设有一个学生选课成绩表,包含学生姓名(stu_name)、课程名称(course_name)和分数(score)三个字段。在原始数据中,每个学生在不同的课程中都有自己的得分情况,数据样例如下:
| stu_name | course_name | score |
|---|---|---|
| 张三 | 数学 | 80 |
| 张三 | 英语 | 85 |
| 张三 | 历史 | 90 |
| 李四 | 数学 | 75 |
| 李四 | 英语 | 92 |
| 李四 | 历史 | 85 |
| 王五 | 数学 | 88 |
| 王五 | 英语 | 90 |
| 王五 | 历史 | 95 |
可以使用行转列操作,将每个学生在不同课程中的分数拆分成多条记录,每条记录包含一个课程以及对应的分数。转换后的数据样例如下:
| stu_name | 数学 | 英语 | 历史 |
|---|---|---|---|
| 张三 | 80 | 85 | 90 |
| 李四 | 75 | 92 | 85 |
| 王五 | 88 | 90 | 95 |
从上表中可以看出,在行转列之后,每一行记录都表示了一个学生在不同课程中的分数。这样更便于对不同科目的分数进行比较、计算平均值等分析操作。
使用case when+group by完成
1 | drop table if exists t_student; |

行转列后的效果是:

sql如下:
当course_name为数学时输出成绩,否则都输出0,然后输出最大值,就筛选出来了,列的名字都是后面起名字获得
1 | select |
通过以上内容的学习,我们这个面试题就迎刃而解了:
1 | select |

第十题

建表
1 | drop table if exists t; |
答案:
1 | select a,(lag(a) over(order by a)) as per_a from t; |
解答上面这个题目需要具备以下知识点:
- lag函数
- lead函数
- row_number函数
lag函数:获取当前行的上一行数据
1 | select empno,ename,sal,(lag(sal) over(order by sal asc)) as pre_sal from emp; |

注意:over函数用来指定“在…..范围内”,通常和lag函数联用。
lead函数:获取当前行的下一行数据
1 | select empno,ename,sal,(lead(sal) over(order by sal asc)) as next_sal from emp; |

注意:over函数用来指定“在…..范围内”,通常和lead函数联用。
row_number函数:可以为查询结果集生成行号:
1 | select empno,ename,sal,row_number() over(order by sal) as rownum from emp; |

利用row_number函数,将两个不相关的列拼接在一起显示:


1 | select |

CTE语法(公用表表达式):Common Table Expression。创建临时表的一种语法:
1 | -- 查询每个部门平均工资的工资等级 |
partition by:将数据分区,和group by区别是:group by是分组,然后和分组函数一起用。partition by分区不需要和分组函数一起使用
1 | select deptno, empno,ename,sal,(lag(sal) over(partition by deptno order by sal asc)) as pre_sal from emp; |

MySQL 8.0及以上版本中支持如下常用的窗口函数:
- ROW_NUMBER():排名函数,返回当前结果集中每个行的行号;
- RANK():排名函数,计算分组结果中的排名,相同的行排名相同且没有空缺,下一个行排名跳过空缺;
- DENSE_RANK():排名函数,计算分组结果中的排名,相同的行排名相同,排名连续,没有空缺;
- NTILE():将分组结果等分为指定的组数,计算每组的大小;
- LAG():返回分组内前一行的值;
- LEAD():返回分组内后一行的值;
- FIRST_VALUE():返回分组内第一个值;
- LAST_VALUE():返回分组内最后一个值;
- AVG()、SUM()、COUNT()、MIN()、MAX():聚合函数,可以配合OVER()进行窗口操作。
需要注意的是,MySQL的窗口函数和其他DBMS中的窗口函数相比较,可能略有不同,需要根据MySQL的文档进行使用。





