先初始化数据,数据来源是http://www.monkey1024.com/database/811
执行以上脚本,我将表名和字段都做了修改,改成小写字母了,为了方便看,其实不改的话看上去更简洁
employee表如下:
department表如下:
salarygrade 表如下:
下面的练习题都是根据上述三张表完成,原题目答案多用连接查询完成,我好想大多用子查询完成的,好像连接查询是比子查询看上去简洁一些:
1取得每个部门最高薪水的人员名称
-- 根据部门编号分组,查询出 部门编号对应的部门最大工资
SELECT departmentnumber ,MAX(salary) FROM employee GROUP BY departmentnumber;
-- 将上述查询当作表和employee表连接,连接条件是两张表的部门编号相同,过滤条件是工资等于部门最高工资
SELECT
employeename,newtab.departmentnumber,newtab.maxsalary
FROM employee e
JOIN (SELECT departmentnumber ,MAX(salary) AS maxsalary FROM employee GROUP BY departmentnumber) AS newtab
ON e.departmentnumber=newtab.departmentnumber
WHERE e.salary=newtab.maxsalary ;
2 哪些人的薪水在部门平均薪水之上
-- 查询部门编号,和部门平均工资
SELECT departmentnumber,AVG(salary) FROM employee GROUP BY departmentnumber;
-- employee表和上述查到的表做连接,连接条件是部门编号相等,过滤条件是员工表的薪水大于新表的部门平均工资
SELECT e.employeename ,e.salary,newtab.departmentnumber,newtab.avgsalary FROM employee e
JOIN
(SELECT departmentnumber,AVG(salary) AS avgsalary FROM employee GROUP BY departmentnumber) AS newtab
ON e.departmentnumber=newtab.departmentnumber
WHERE e.salary >newtab.avgsalary;
3取得部门中(所有人的)平均薪水等级
--第一步按部门编号分组,查询到部门编号和部门平均薪水
--第二部 将薪水表和上述新表做连接,条件是新表的平均薪水在薪水表中的最低薪水和最高薪水之间
SELECT newtab.departmentnumber,s.grade FROM salarygrade AS s
JOIN
(SELECT departmentnumber, AVG(salary) avgsal FROM employee GROUP BY departmentnumber) newtab
ON newtab.avgsal BETWEEN s.lowsalary AND s.highsalary;
4 不用max函数,取得最高薪水
-- 法1:将薪水按降序排列,取第一个即可
SELECT salary AS maxsalary FROM employee ORDER BY salary DESC LIMIT 1;
5 取得平均薪水最高的部门的部门编号
-- 法1:查询部门编号和部门的平均工资,按降序排列取第一条记录
SELECT departmentnumber,AVG(salary) AS avgsalary FROM employee GROUP BY departmentnumber ORDER BY avgsalary DESC LIMIT 0,1;
-- 法2:1查询部门编号的部门平均工资
-- 2 在上述临时表中查出最大的部门平均工资
-- 3 从员工表中查部门编号,条件是按部门编号分组后的部门平均工资等于第2步的结果
SELECT departmentnumber,AVG(salary) avgsal FROM employee GROUP BY departmentnumber
HAVING avgsal=(
SELECT MAX(newtab.avgsalary)
FROM
(SELECT departmentnumber,AVG(salary) AS avgsalary FROM employee GROUP BY departmentnumber) newtab);
6取得平均薪水最高的部门的部门名称
-- 法1:1 查询部门编号和平均工资,按平均工资降序排列,取第一条,即得到平均工资最大的部门编号和部门平均工资
-- 2 将上述临时表和部门表连接查询,条件是两表的部门编号相同
SELECT d.departmentnumber,d.departmentname FROM department d
JOIN
(SELECT departmentnumber, AVG(salary) AS avgsalary FROM employee GROUP BY departmentnumber ORDER BY avgsalary DESC LIMIT 0,1) newtab
ON d.departmentnumber=newtab.departmentnumber;
-- 法2:1在employee表中按部门编号分组,查询部门编号和部门平均工资,降序取第1条记录中的部门编号
-- 在部门表中查询,条件是部门编号等于第1步的部门编号
SELECT departmentname FROM department
WHERE
departmentnumber =(SELECT departmentnumber FROM employee GROUP BY departmentnumber ORDER BY AVG(salary) DESC LIMIT 1);
说明:此题演示了子查询和连接查询没有本质区别,子查询出的结果可以作为一张临时表和其他表做查询
如果查询出的数据只有一列,可以用来做子查询,有多列的话只能用来连接查询
7 求平均薪水的等级最低的部门的部门名称
--1 查询部门编号和部门平均薪水
SELECT departmentnumber,AVG(salary) FROM employee GROUP BY departmentnumber;
-- 2 薪水表和部门表和第1步的临时表,3张表做连接查询
-- 查到每个部门的平均薪水的等级,和部门编号,部门名称
SELECT t1.departmentnumber ,d.departmentname,s.grade FROM salarygrade s
JOIN
(SELECT departmentnumber,AVG(salary) avgsal FROM employee GROUP BY departmentnumber) t1
ON
t1.avgsal BETWEEN s.lowsalary AND highsalary
JOIN
department d
ON t1.departmentnumber=d.departmentnumber;
--3 查询到平均薪水等级中最低的等级,将按部门编号分组后求得的平均薪水和薪水表连接查询,找出最低的部门平均薪水等级
SELECT MIN(s.grade)
FROM (SELECT AVG(salary) AS avgsalary FROM employee e GROUP BY departmentnumber ) t
JOIN
salarygrade s
ON
t.avgsalary BETWEEN s.lowsalary AND s.highsalary;
-- 4 从第2步的结果中查询最低等级对应的部门名称,即从第2步生成的临时表中查平均薪水等级最低的部门名称,条件是第2步临时表中部门编号等于第3步的结果
SELECT tt1.departmentname
FROM
(
SELECT t1.departmentnumber ,d.departmentname,s.grade FROM salarygrade s
JOIN
(SELECT departmentnumber,AVG(salary) avgsal FROM employee GROUP BY departmentnumber) t1
ON
t1.avgsal BETWEEN s.lowsalary AND highsalary
JOIN
department d
ON t1.departmentnumber=d.departmentnumber ) tt1
WHERE
tt1.grade=(SELECT MIN(s.grade)
FROM (SELECT AVG(salary) AS avgsalary FROM employee e GROUP BY departmentnumber ) t
JOIN
salarygrade s
ON
t.avgsalary BETWEEN s.lowsalary AND s.highsalary ) ;
8取得比普通员工(员工编号没有在mgr上出现的)的最高薪水还要高的经理人(是指出现在manager字段的员工编号的)姓名
--1在employee表 查询manager编号,去除null元素
SELECT DISTINCT manager FROM employee WHERE manager IS NOT NULL;
-- 2查询员工编号没在manager列出现过的员工的最大工资
SELECT MAX(salary) FROM employee WHERE employeenumber NOT IN (SELECT DISTINCT manager FROM employee WHERE manager IS NOT NULL);
-- 3先将manager编号查询出来作为临时表,和employee做连接查询,连接条件是employee表中的员工编号等于临时表的manager编号,
-- (说明这些员工编号都是manager出现过的人),过滤条件是这些manager中工资大于第2步结果
SELECT e1.employeename
FROM employee e1
JOIN
(SELECT DISTINCT(manager) AS mgr FROM employee WHERE manager IS NOT NULL ) AS t
ON e1.employeenumber=t.mgr
WHERE e1.salary>(
SELECT MAX(salary) FROM employee WHERE employeenumber NOT IN (SELECT DISTINCT manager FROM employee WHERE manager IS NOT NULL)
);
9取得每个薪水等级有多少员工
-- 1 employee表和薪水等级表连接查询出员工编号和薪水等级
SELECT e.employeenumber AS empn,s.grade AS grade FROM employee e JOIN salarygrade s ON e.salary BETWEEN s.lowsalary AND s.highsalary;
-- 2 从第1步生成的临时表中查询出工资等级和每个等级的人数
SELECT grade,COUNT(empn)
FROM
(SELECT e.employeenumber AS empn,s.grade AS grade
FROM employee e
JOIN salarygrade s
ON e.salary BETWEEN s.lowsalary AND s.highsalary) AS newtab
GROUP BY grade;
10列出受雇日期早于其直接上级的所有员工编号、姓名
-- 两张employee表做内连接,连接条件表1的manager等于表2的员工编号,过滤条件是员工的受雇日期小于manager的受雇日期
SELECT e1.employeenumber ,e1.employeename
FROM employee e1
JOIN employee e2
ON e1.manager=e2.employeenumber
WHERE e1.hiredate<e2.hiredate;
11列出至少有5个员工的部门名称
-- 1在employee表中按员工编号分组,找出雇员大于5的部门编号
SELECT departmentnumber FROM employee GROUP BY departmentnumber HAVING COUNT(employeename) >=5;
-- 2第1步生成的临时表和部门表做连接查询,连接条件是部门编号相等,查出部门编号和部门名称
SELECT d.departmentnumber,d.departmentname
FROM department d
JOIN
(SELECT departmentnumber FROM employee GROUP BY departmentnumber HAVING COUNT(employeename)>=5) AS newtab
ON d.departmentnumber=newtab.departmentnumber;
12列出最低薪水大于1500的工作及从事此工作的雇员人数
-- 在employee表中按工作分组,查询工作名称和该工作的最低工资,以及该工作的员工数
--过滤条件是该工作的员工数大于等于5
SELECT job ,MIN(salary) minsalary ,COUNT(*) FROM employee GROUP BY job HAVING minsalary>1500;
13列出在部门“SALES”工作的员工的姓名
-- 第1步,查询部门名称是'SALES'的部门编号
--2 从employee表中查询员工姓名,过滤条件是其部门编号等于第1步子查询的结果
SELECT employeename FROM employee WHERE departmentnumber=(SELECT departmentnumber FROM department WHERE departmentname='SALES');
14求部门名称中带“S”字符的部门员工的工资合计、部门人数
-- 1从department表中查询出部门名称中带'S'的部门编号
SELECT departmentnumber FROM department WHERE departmentname LIKE '%S%';
-- 2 将employee表和第1步生成的临时表做右连接,条件是部门编号相等,再按临时表的部门编号分组,查询出部门名称,部门工资之和,部门员工数
SELECT newtab.departmentname,SUM(e.salary),COUNT(e.employeename)
FROM employee e
RIGHT JOIN
(SELECT departmentnumber ,departmentname FROM department WHERE departmentname LIKE '%S%') AS newtab
ON e.departmentnumber=newtab.departmentnumber GROUP BY newtab.departmentname ;
--法2 将employee表和department表做右连接,连接条件是部门编号相等,过滤条件是部门名称中带'S'的部门,再按部门名称分组,查询出部门名称,部门工资之和,和部门员工数
SELECT d.departmentname,SUM(salary),COUNT(e.employeename)
FROM employee e
RIGHT JOIN
department d
ON e.departmentnumber=d.departmentnumber
WHERE d.departmentname LIKE '%S%'
GROUP BY d.departmentname ;
15
有3个表S(学生表),C(课程表),SC(学生选课表),其中
S(SNO,SNAME)代表(学号,姓名)
C(CNO,CNAME,CTEACHER)代表(课号,课名,教师)
SC(SNO,CNO,SCGRADE)代表(学号,课号,成绩)
问题:
- 找出没选过“黎明”老师的所有学生姓名。
- 列出2门以上(含2门)不及格学生姓名及平均成绩。
- 即学过1号课程又学过2号课所有学生的姓名。
初始化数据:
DROP TABLE IF EXISTS SC;
CREATE TABLE SC(
SNO VARCHAR(200),
CNO VARCHAR(200),
SCGRADE VARCHAR(200)
);
DROP TABLE IF EXISTS S;
CREATE TABLE S(
SNO VARCHAR(200 ),
SNAME VARCHAR(200)
);
DROP TABLE IF EXISTS C;
CREATE TABLE C(
CNO VARCHAR(200),
CNAME VARCHAR(200),
CTEACHER VARCHAR(200)
);
INSERT INTO C ( CNO, CNAME, CTEACHER ) VALUES ( '1', '语文', '张');
INSERT INTO C ( CNO, CNAME, CTEACHER ) VALUES ( '2', '政治', '王');
INSERT INTO C ( CNO, CNAME, CTEACHER ) VALUES ( '3', '英语', '李');
INSERT INTO C ( CNO, CNAME, CTEACHER ) VALUES ( '4', '数学', '赵');
INSERT INTO C ( CNO, CNAME, CTEACHER ) VALUES ( '5', '物理', '黎明');
INSERT INTO S ( SNO, SNAME ) VALUES ( '1', '学生1');
INSERT INTO S ( SNO, SNAME ) VALUES ( '2', '学生2');
INSERT INTO S ( SNO, SNAME ) VALUES ( '3', '学生3');
INSERT INTO S ( SNO, SNAME ) VALUES ( '4', '学生4');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '1', '1', '40');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '1', '2', '30');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '1', '3', '20');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '1', '4', '80');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '1', '5', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '2', '1', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '2', '2', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '2', '3', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '2', '4', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '2', '5', '40');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '3', '1', '60');
INSERT INTO SC ( SNO, CNO, SCGRADE ) VALUES ( '3', '3', '80');
1找出没选过“黎明”老师的所有学生姓名。
--1 查询出黎明老师的可号
SELECT CNO FROM C WHERE CTEACHER='黎明';
-- 2先找出选过黎明老师课号的学生学号
SELECT DISTINCT SNO FROM SC WHERE CNO NOT IN (SELECT CNO FROM C WHERE CTEACHER='黎明');
--3从学生表上找学号不等于上述学号的学生,(有些学生可能没在选课表上)
SELECT SNAME FROM S WHERE SNO NOT IN (SELECT DISTINCT SNO FROM SC WHERE CNO IN (SELECT CNO FROM C WHERE CTEACHER='黎明'));
2 列出2门以上(含2门)不及格学生姓名及平均成绩
-- 1 在SC表上按学号分组,查找出不及格数大于等于两门的学号
SELECT SNO,COUNT(*) FROM SC WHERE SCGRADE<60 GROUP BY SNO HAVING COUNT(*)>=2;
-- 2 在S表上查找学生姓名,条件是学号在第一步查询的结果之中
SELECT SNAME FROM S WHERE SNO IN (SELECT SNO FROM SC WHERE SCGRADE<60 GROUP BY SNO HAVING COUNT(*)>=2);
-- 3 将S表和第1步的临时表再和SC表做连接查询,连接条件是学号相等,再按学号分组,找出该学号的姓名,平均成绩
SELECT SNAME,AVG(scgrade)
FROM S
JOIN (SELECT SNO FROM SC WHERE SCGRADE<60 GROUP BY SNO HAVING COUNT(*)>=2) AS newtab
ON S.SNO=newtab.SNO
JOIN SC
ON newtab.SNO=SC.SNO
GROUP BY SC.SNO;
3即学过1号课程又学过2号课所有学生的姓名。
-- 1 在SC表中查询选过课号为1的学号
SELECT SNO FROM SC WHERE CNO=1;
-- 2在SC表上查询选过课号为2的学号
SELECT SNO FROM SC WHERE CNO=2;
-- 3 将上述两张临时表做连接查询,连接条件是学号相等,查询两张临时表交集的学号,
-- 再从S表中查找学生姓名,条件是学号等于前面查到的学号
SELECT SNAME FROM S WHERE SNO
IN (SELECT t1.SNO FROM
(SELECT SNO FROM SC WHERE CNO=1) t1
JOIN
(SELECT SNO FROM SC WHERE CNO=2)t2
ON
t1.SNO=t2.SNO);
参考:小猴子视频