一组数据表练习Mysql查询
一组数据表练习Mysql查询
最近银行和金融相关企业也陆续开始笔面试流程了。相比互联网企业,它们的开发岗位对数据分析也有一定的要求。因此在这里对SQL查询相关技巧进行由浅入深的练习回顾。
数据表格式
数据库模式如下(分别为Department,Employee,Category,Project,Workson):
部门(部门号,部门名称,位置)
员工(员工号,姓名,年龄,性别,所在部门号)
项目种类(项目种类号,项目种类名)
项目(项目号,项目名称,预算,项目种类号)
员工工作情况(员工号,项目号,职责,开始日期)
下载地址:
https://github.com/Lunaticsky-tql/SQLQueryPractice
某年期末考试题
一共10道,难度递增。
1.给出职工中所有男性的所有信息(empid,empname,age,sex,edpid)
SELECT * FROM employee
WHERE sex='男'
2.统计来自天津的“李”姓职工信息,按年龄降序排序(empid,empname,age,location)
SELECT empid,empname,age,location
FROM employee,department
WHERE employee.empname LIKE '李%' AND employee.depid =department.depid
ORDER BY age DESC
3.给出每位职员参与项目的最高预算和最低预算(empname,highestbudget,lowestbudget)
SELECT empname,MAX (budget) AS higestbudget, MIN (budget) AS lowestbudget
FROM employee ,workson ,project
WHERE project.proid =workson.proid AND employee.empid=workson.empid
GROUP BY empname
4.给出所有项目超过一个的员工的id和参加的项目个数(empid, num)
select empid,count(proid) as num
from workson
having count(proid)>1
group by empid
5.给出项目种类号为“c2”且预算最多的项目。(proid,projectname,budget)
select proid,projectname,budget
from project
where budget>=all(select budget from project where catid ='c2') and catid='c2'
6.给出参加“产品推广”项目,但不担任职位的员工的员工信息,(empid,empname,age,sex.depid)
select empid,empname,age,sex,depid
from employee
where empid in(select empid from workson where proid=(select proid from project where projectname='产品推广') and job is null)
7.给出工号为“10102”的员工每类项目的参加总数,若没有参加过某类项目,则参加项目总数显示为0(catid,proNum)
select catid,count(workson.proid) as proNum
from project,workson
where empid=10102 and project.proid=workson.proid
group by catid
order by catid
但是随后发现这种方式并不能满足”若没有参加过某类项目,则参加项目总数显示为0“的要求。
修改如下:
select distinct category.catid ,isnull(subtable.subNum) as proNum
from category left join (select catid,count(workson.proid) as subNum
from project,workson
where empid=10211 and project.proid=workson.proid
group by catid) as subtable
on category.catid=subtable.catid
以10211为例进行了测试:成功。
distinct操作往往比较耗费性能。
8.给出没有参与“软件类”项目女性职工的信息(empid,empname, age,sex,depid)
SELECT *
FROM employee
WHERE sex = '女'
AND NOT EXISTS (
SELECT *
FROM workson
WHERE empid = employee.empid
AND proid IN (
SELECT proid
FROM project
WHERE catid = (
SELECT catid
FROM category
WHERE catname = '软件类'
)
)
)
刚刚发现了格式化的功能。
9.给出有30岁以上男性员工的省份名称和该省男员工最大年龄,结果按最大年龄升序排序
SELECT location, MAX(age)
FROM department, employee
WHERE department.depid = employee.depid
AND sex = '男'
GROUP BY location
HAVING MAX(age) > 30
ORDER BY MAX(age)
10.给出在广州工作的、参加“'产品推广'”项目的职员id、姓名及他们参加的项目个数(empid, empname, procnt)
SELECT employee.empid, empname, count(workson.proid) AS procnt
FROM project, employee, workson, department
WHERE location = '广州'
AND projectname = '产品推广'
AND department.depid = employee.depid
AND workson.proid = project.proid
AND employee.empid = workson.empid
GROUP BY employee.empid
补充题目
根据数据库课程讲述内容和网上资料进行整理,往往是比较容易遗忘或犯错的地方,难度不一。标*的题目有超出课程范围内容。
1.隶属同一部门的员工对。(name1,name2)
select e1.empname as name1,e2.empname as name2
from employee e1,employee e2 where e1.depid=e2.depid and e1.empname<e2.empname
利用小于关系去重
- 查找部门中只有一名员工的员工姓名(empname)
select empname
from employee e1
where not exists (select * from employee
where depid=e1.depid and empname<>e1.empname)
- 2020年12月21日之后入职的员工所参与项目的平均预算(empid,avgngbudget)
select empid,avg(budget) as avgbudget
from workson,project
where workson.proid=project.proid
and enterdate >'2020-12-21 00:00'
group by empid
- 如果担任过职员的员工所参与的所有项目的平均预算大于110000则返回结果为职员的名字(empname),与对应的平均项目预算值avg(budget)
select empname,avg(budget)
from employee natural join workson natural join project
where empid in (select empid from workson where job='职员')
group by empname
having avg(budget)>110000;
典型错误:
sql
select empname,avg(budget) from employee natural join workson natural join project where job='职员' group by empname having avg(budget)>110000;
错误原因:这时候avg只计算了参与角色为职员的项目。
- *找出每个部门预算前两名的项目,前两名指不同 预算中 排名前两名,若部门少于两个项目,返回部门内所有项目。(catname,projectname,budget)。
select catname,projectname,budget from project p1 join category c on p1.catid=c.catid
where
2>(select count(distinct p2.budget)
from project p2
where p1.budget<p2.budget and p1.catid=p2.catid)
注:这是经典的分组TopK问题。
分解子任务为在project表中找每个catid中排名前三的project。
对于这个子问题,用到了相关子查询的技巧。
将project表克隆一份,在原表上遍历,判断是否有大于两个部门的预算大于它。最后与category 连接即可。
MySQL8之后可以使用窗口函数完成类似的功能,可以参见这篇文章学习。
窗口函数的做法:
select catname,projectname,budget,
dense_rank() over(partition by c.catid order by budget desc)
from category c join project p on c.catid=p.catid
最终得到可行SQL语句:
select t.catname,t.projectname,t.budget from
(select catname,projectname,budget,
dense_rank() over(partition by c.catid order by budget desc) as rk
from category c join project p on c.catid=p.catid) t
where t.rk<=2
- *找出每个部门项目预算的中位数,若有两个中间数,返回两者而非取平均值。(catname,projectname,budget)。
本题目较容易理解的方法需要掌握上一个题目中提到的窗口函数。当然也有其他解法,但较繁琐或使用较多其他的内置函数。下面对使用窗口函数的方法进行讲解。
总的思路是利用中位数在排序中的位次关系求解。
select
catname,projectname,budget,
row_number() over(partition by catid order by budget) as rk,
count(proid) over(partition by catid) as n
from category c natural join project p
使用partion_by
分组统计每一部门的排名,并使用count
得出该部门的部门数,方便后续筛选。
得到下面的中间结果。
作为中位数,其rank应位于[n/2,n/2+1]
之间。
比较违反编程直觉的是,MySQL中除法是浮点数除法,例如有三个元素,区间为[1.5,2.5]
,有四个元素,区间为[2,3]
。可以看到奇数时包含一个元素,偶数时包含两个元素,符合题目要求。
最终得到可行SQL如下:
select catname,projectname,budget
from
(select
catname,projectname,budget,
row_number() over(partition by catid order by budget) as rk,
count(proid) over(partition by catid) as n
from category c natural join project p
)t
where rk>=n/2 and rk<=n/2+1
- 查找每个项目类别超过其内项目平均预算的项目?(catname,projectname)
有了TopK问题解法一的基础,就很容易想到用相关子查询来做了。
对于这个题来说,相当于先在x表内找到对应的项目类别,再在子查询中返回该类别中的项目平均预算,最后回到父查询得到该类别中大于平均预算的项目。类似于二重循环。
select catname,projectname
from project x natural join category c
where budget >=(select avg(budget)
from project y
where y.catid =x.catid
group by y.catid )
- 参与过所有项目的员工的姓名(empname)。
select empname from employee
where not exists(select * from project
where not exists (select * from workson
where empid=employee.empid and proid=project.proid))
思路依旧是循环。最内重子查询去遍历workson表中每个employee参与的每个项目,第二层子查询得到的是该员工没参与过的项目。总的语句含义是一个双重否定,即:不存在这个职员没参加过的项目。