文章目录
- 简介
- 初始化表
- 练习题
简介
本节简介:
主要是一些给出一些习题, 关于DQL查询相关的, DQL查询语句是最重要的SQL语句, 功能性最复杂, 功能也最强, 所以本节建议适合以及有了DQL查询基础的食用, 另外注意我们使用的是Navicat, SQL编辑的格式规范也是Navicat指定的默认格式, 不是dos命令行窗口…
初始化表
DROP TABLE IF EXISTS EMP;
DROP TABLE IF EXISTS DEPT;
DROP TABLE IF EXISTS SALGRADE;CREATE TABLE DEPT(DEPTNO int(2) not null ,DNAME VARCHAR(14) ,LOC VARCHAR(13),primary key (DEPTNO)
);
CREATE TABLE EMP(EMPNO int(4) not null ,ENAME VARCHAR(10),JOB VARCHAR(9),MGR INT(4),HIREDATE DATE DEFAULT NULL,SAL DOUBLE(7,2),COMM DOUBLE(7,2),primary key (EMPNO),DEPTNO INT(2)
);CREATE TABLE SALGRADE( GRADE INT,LOSAL INT,HISAL INT
);INSERT INTO DEPT ( DEPTNO, DNAME, LOC ) VALUES ( 10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO DEPT ( DEPTNO, DNAME, LOC ) VALUES ( 20, 'RESEARCH', 'DALLAS');
INSERT INTO DEPT ( DEPTNO, DNAME, LOC ) VALUES ( 30, 'SALES', 'CHICAGO');
INSERT INTO DEPT ( DEPTNO, DNAME, LOC ) VALUES ( 40, 'OPERATIONS', 'BOSTON'); INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,DEPTNO ) VALUES ( 7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800, NULL, 20);
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,DEPTNO ) VALUES ( 7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600, 300, 30);
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,DEPTNO ) VALUES ( 7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250, 500, 30);
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,DEPTNO ) VALUES ( 7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975, NULL, 20);
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,DEPTNO ) VALUES ( 7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250, 1400, 30);
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,DEPTNO ) VALUES ( 7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850, NULL, 30);
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,DEPTNO ) VALUES ( 7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450, NULL, 10);
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,DEPTNO ) VALUES ( 7788, 'SCOTT', 'ANALYST', 7566, '1987-04-19', 3000, NULL, 20);
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,DEPTNO ) VALUES ( 7839, 'KING', 'PRESIDENT', NULL, '1981-11-17', 5000, NULL, 10);
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,DEPTNO ) VALUES ( 7844, 'TURNER', 'SALESMAN', 7698, '1981-09-08', 1500, 0, 30);
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,DEPTNO ) VALUES ( 7876, 'ADAMS', 'CLERK', 7788, '1987-05-23', 1100, NULL, 20);
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,DEPTNO ) VALUES ( 7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950, NULL, 30);
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,DEPTNO ) VALUES ( 7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000, NULL, 20);
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,DEPTNO ) VALUES ( 7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1300, NULL, 10); INSERT INTO SALGRADE ( GRADE, LOSAL, HISAL ) VALUES ( 1, 700, 1200);
INSERT INTO SALGRADE ( GRADE, LOSAL, HISAL ) VALUES ( 2, 1201, 1400);
INSERT INTO SALGRADE ( GRADE, LOSAL, HISAL ) VALUES ( 3, 1401, 2000);
INSERT INTO SALGRADE ( GRADE, LOSAL, HISAL ) VALUES ( 4, 2001, 3000);
INSERT INTO SALGRADE ( GRADE, LOSAL, HISAL ) VALUES ( 5, 3001, 9999);
commit;
上面就是本节所需的SQL脚本文件, 在联系下面的习题之前需要执行一下上面的SQL
简单解释一下这三张表
-- emp员工表, 存储的员工的一些信息数据, 分别是
-- 员工编号, 员工姓名, 工作, 上级领导编号, 入职日期, 薪水, 津贴, 部门编号desc emp;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| EMPNO | int | NO | PRI | NULL | |
| ENAME | varchar(10) | YES | | NULL | |
| JOB | varchar(9) | YES | | NULL | |
| MGR | int | YES | | NULL | |
| HIREDATE | date | YES | | NULL | |
| SAL | double(7,2) | YES | | NULL | |
| COMM | double(7,2) | YES | | NULL | |
| DEPTNO | int | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+-- dept部门表, 存储的是部门的一些信息
-- 部门编号, 部门名称, 地址desc dept;
+--------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| DEPTNO | int | NO | PRI | NULL | |
| DNAME | varchar(14) | YES | | NULL | |
| LOC | varchar(13) | YES | | NULL | |
+--------+-------------+------+-----+---------+-------+-- salgrade薪资水平表, 存储的每个级别的薪资对应的薪资范围
-- 等级, 最低薪资(该等级), 最高薪资(该等级)
desc salgrade;
+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| GRADE | int | YES | | NULL | |
| LOSAL | int | YES | | NULL | |
| HISAL | int | YES | | NULL | |
+-------+------+------+-----+---------+-------+
练习题
第一题 : 取得每个部门最高薪水的人员名称
-- 取得每个部门最高薪水的人员名称
-- step1 : 按照部门编号分组求出来每个部门的最高薪水
SELECTdeptno,max( sal ) AS max_sal
FROMemp
GROUP BYdeptno;-- step2 : 因为要查询人员的信息, 所以把上面step1中查询的结果当作临时表t链接emp查询
SELECTe.ename,e.sal,e.deptno,t.max_sal
FROM( SELECT deptno, max( sal ) AS max_sal FROM emp GROUP BY deptno ) tJOIN emp e ON t.max_sal = e.sal AND t.deptno = e.deptno;
执行结果
第二题 : 那些人的薪水在部门的平均薪水之上
-- 第二题 : 那些人的薪水在部门的平均薪水之上
-- step1 : 按照部门的编号进行分组然后求出来部门的平均薪水
SELECTdeptno,avg( sal ) AS avg_sal
FROMemp
GROUP BYdeptno;
-- step2 : 把上面的查询结果当作临时表, 与emp员工表进行链接
SELECTe.ename,e.sal,e.deptno,t.avg_sal
FROM( SELECT deptno, avg( sal ) AS avg_sal FROM emp GROUP BY deptno ) tJOIN emp e ON e.sal > t.avg_sal AND e.deptno = t.deptno;
执行结果
第三题 : 取得每个部门平均薪水的等级
-- 第三题 : 取得每个部门平均薪水的等级
-- step1 : 首先按照部门分组然后找到每一个部门的平均薪资
SELECTdeptno,avg( sal ) AS avg_sal
FROMemp
GROUP BYdeptno;
-- step2 : 把上面的表看作是一个临时表t然后与薪资等级表salgrade进行链接
SELECTt.deptno,t.avg_sal,s.grade
FROM( SELECT deptno, avg( sal ) AS avg_sal FROM emp GROUP BY deptno ) tJOIN salgrade s ON t.avg_sal BETWEEN s.losal AND s.hisal;
执行结果
第四题 : 取得部门中(所有人的)平均的薪水等级
在这个题之前我们澄清一下小小的误区就是, 在表进行链接之后, 其实就相当于一张表了, 所以可以对链接的表的字段进行分组, 并且分组函数也可以操作链接的表的字段
-- 第四题 : 取得部门中(所有人的)平均的薪水等级
-- step1 : 通过表的链接直接找到所有人的薪水等级
SELECTs.grade,e.deptno
FROMemp eJOIN salgrade s ON e.sal BETWEEN s.losal AND s.hisal;
-- step2 : 在上面的表的基础上直接进行deptno分组然后分组函数直接作用于grade字段
SELECTavg( s.grade ),e.deptno
FROMemp eJOIN salgrade s ON e.sal BETWEEN s.losal AND s.hisal
GROUP BYe.deptno;
执行结果
第五题 : 不准用组函数(Max),取得最高薪水(给出两种解决方案)
方案一 : limit方案
-- 方法1 : Limit方案
SELECTsal
FROMemp
ORDER BYsal DESC LIMIT 1;
方法2 : 自连接方案
-- 方案2 : 子链接方案, 首先用自链接查出来除了最大薪资的所以薪资然后用not in判断
-- step1 : 首先去除最大薪资的集合
SELECT DISTINCTa.sal
FROMemp aJOIN emp b ON a.sal < b.sal;
-- step2 : 用not in进行过滤
SELECTsal
FROMemp
WHEREsal NOT IN ( SELECT DISTINCT a.sal FROM emp a JOIN emp b ON a.sal < b.sal );
执行结果均为下图
第六题 : 取得平均薪水最高的部门的部门编号
-- 第六题 : 取得平均薪水最高的部门的部门编号(至少给出两种解决方案)
-- step1 : 取到每一个部门的平均薪水
SELECTavg( sal ) AS avg_sal
FROMemp
GROUP BYdeptno;
-- step2 : 把上面的表当作是临时表然后用max函数查到最大的平均薪水
SELECTmax( t.avg_sal )
FROM( SELECT avg( sal ) AS avg_sal FROM emp GROUP BY deptno ) t;
-- step3 : 把上面的表再次当作临时表然后找到step1中的平均信息进行筛选
SELECTtt.deptno
FROM( SELECT deptno, avg( sal ) AS avg_sal FROM emp GROUP BY deptno ) tt
WHEREtt.avg_sal IN ( SELECT max( t.avg_sal ) FROM ( SELECT avg( sal ) AS avg_sal FROM emp GROUP BY deptno ) t );
本题贴士 : limti不可以在子查询当中使用
执行结果为下图
第七题 : 取得平均薪水最高的部门的部门名称
-- 第七题 : 取得平均薪水最高的部门的部门名称
-- 这道题跟上一题的区别就是我们进行表连接之后进行分组
-- step1 : 进行连接之后按照部门名称分组
SELECTd.dname,avg( sal )
FROMemp eJOIN dept d ON e.deptno = d.deptno
GROUP BYd.dname;
-- step2 : 把上面的表当作临时表然后用分组函数找到最大值
SELECTmax( avg_sal )
FROM(SELECTd.dname,avg( sal ) AS avg_sal FROMemp eJOIN dept d ON e.deptno = d.deptno GROUP BYd.dname ) t;
-- step3 : 根据step1中的表和step2的最大值进行筛选
SELECTtt.dname
FROM(SELECTd.dname,avg( sal ) AS avgSal FROMemp eJOIN dept d ON e.deptno = d.deptno GROUP BYd.dname ) tt
WHEREtt.avgSal IN (SELECTmax( avg_sal ) FROM(SELECTd.dname,avg( sal ) AS avg_sal FROMemp eJOIN dept d ON e.deptno = d.deptno GROUP BYd.dname ) t );
本题贴士 :
- 不允许出现tt.t.name(也就是临时表名字嵌套临时表名字的写法)
- 在进行了表连接之后, 其实就相当于一张表, 所以可以对连接表的字段进行分组, 同时可以运用分组函数
执行结果如下
第九题 : 取得比普通员工(员工代码没有在mgr字段上出现的)的最高薪水还要高的领导人姓名
-- 第九题 : 取得比普通员工(员工代码没有在mgr字段上出现的)的最高薪水还要高的领导人姓名
-- step1 : 找到mgr中出现的所有员工编号(都是当过领导人的)
SELECT DISTINCTmgr
FROMemp
WHEREmgr IS NOT NULL;
-- step2 : 用step1中得到的姓名编号再次进行筛选
SELECTempno
FROMemp
WHEREempno NOT IN ( SELECT DISTINCT mgr FROM emp WHERE mgr IS NOT NULL );
-- step3 : 找到这些员工的最高的薪资
SELECTmax( sal )
FROMemp
WHEREempno IN (SELECTempno FROMemp WHEREempno NOT IN ( SELECT DISTINCT mgr FROM emp WHERE mgr IS NOT NULL ));
-- step4 : 找到step2中找到的领导编号的信息
SELECTename
FROMemp
WHEREempno IN ( SELECT DISTINCT mgr FROM emp WHERE mgr IS NOT NULL ) AND sal > (SELECTmax( sal ) FROMemp WHEREempno IN (SELECTempno FROMemp WHEREempno NOT IN ( SELECT DISTINCT mgr FROM emp WHERE mgr IS NOT NULL )));
执行结果如下
第十题 : 取得薪水最高的前五名员工
-- 第十题 : 取得薪水最高(带补贴)的前五名员工
SELECTempno,ename,sal + ifnull( comm, 0 ) AS all_sal
FROMemp
ORDER BYall_sal DESC LIMIT 5;
执行结果如下
第十一题 : 取得薪水最高的第六到第十名员工
-- 第十一题 : 取得薪水最高的第六到第十名员工
SELECTempno,ename,sal
FROMemp
ORDER BYsal DESC LIMIT 5,5;
执行结果如下
第十二题 : 取得最后入职的5名员工
-- 第十二题 : 取得最后入职的5名员工
SELECTempno,ename,hiredate
FROMemp
ORDER BYhiredate DESC LIMIT 5;
执行结果如下
第十三题 : 取得每个薪水等级有多少员工
-- 第十三题 : 取得每个薪水等级有多少员工
-- step1 : 表连接找到每个员工的薪水等级
SELECTe.ename,e.sal,s.grade
FROMemp eJOIN salgrade s ON e.sal BETWEEN s.losal AND s.hisal;
-- step2 : 在step1的表的基础上直接对grade进行分组并计数
SELECTs.grade,count(*)
FROMemp eJOIN salgrade s ON e.sal BETWEEN s.losal AND s.hisal
GROUP BYs.grade;
执行结果如下
第十四题 : 列出所有员工及领导的姓名
-- 第十四题 : 列出所有员工及领导的姓名
-- step1 : 其实就是一个子链接 + 左连接
SELECTe.ename,l.ename
FROMemp eLEFT JOIN emp l ON e.mgr = l.empno;
执行结果如下
第十五题 : 列出受雇日期早于其直接上级的所有员工的编号,姓名,部门名称
-- 第十五题 : 列出受雇日期早于其直接上级的所有员工的编号,姓名,部门名称
-- step1 : 首先把员工表进行子链接(内连接, 此时不是外连接)
SELECT*
FROMemp eJOIN emp l ON e.mgr = l.empno;
-- step2 : 把step1中的信息与部门名称继续连接, 并添加where条件过滤
SELECTe.empno,e.ename,d.dname
FROMemp eJOIN emp l ON e.mgr = l.empnoJOIN dept d ON d.deptno = e.deptno
WHEREe.hiredate < l.hiredate;
执行结果如下
第十六题 : 列出部门名称和这些部门的员工信息,同时列出那些没有员工的部门
-- 第十六题 : 列出部门名称和这些部门的员工信息,同时列出那些没有员工的部门
-- step1 : 把emp表跟dept表进行外连即可
SELECTd.dname,e.*
FROMemp eRIGHT JOIN dept d ON e.deptno = d.deptno;
执行结果如下
第十七题 : 列出至少有5个员工的所有部门
-- 第十七题 : 列出至少有5个员工的所有部门
-- step1 : 首先进行表连接
SELECT*
FROMemp eJOIN dept d ON e.deptno = d.deptno;
-- step2 : 在step1的基础上进行分组having过滤
SELECTd.dname,count(*)
FROMemp eJOIN dept d ON e.deptno = d.deptno
GROUP BYd.dname
HAVINGcount(*) >= 5;
执行结果如下
第十八题 : 列出薪金比"SMITH"多的所有员工信息
-- 第十八题 : 列出薪金比"SMITH"多的所有员工信息
-- step1 : 首先找到'smith'的薪资
SELECTsal
FROMemp
WHEREename = 'smith';
-- step2 : 运用子查询找到薪资比smith高的员工信息
SELECT empno, ename, sal
FROM emp
WHERE sal > ( SELECT sal FROM emp WHERE ename = 'smith' );
执行结果如下
第十九题 : 列出所有"CLERK"(办事员)的姓名及其部门名称,部门的人数
-- 第十九题 : 列出所有"CLERK"(办事员)的姓名及其部门名称,部门的人数
-- step1 : 首先找到每个部门的名称以及部门的人数
SELECTd.dname,count(*)
FROMemp eJOIN dept d ON e.deptno = d.deptno
GROUP BYd.dname;
-- step2 : 找到所有job = CLERK的人员的信息
SELECTe.ename,e.job,e.deptno,d.dname
FROMemp eJOIN dept d ON e.deptno = d.deptno
WHEREjob = 'CLERK';
-- step3 : 把上面的两张表都看作是临时表进行连接
SELECTt2.ename,t2.dname,t1.cnt
FROM(SELECTd.dname,count(*) AS cnt FROMemp eJOIN dept d ON e.deptno = d.deptno GROUP BYd.dname ) t1JOIN (SELECTe.ename,e.job,e.deptno,d.dname FROMemp eJOIN dept d ON e.deptno = d.deptno WHEREjob = 'CLERK' ) t2 ON t1.dname = t2.dname;
执行结果如下
第二十题 : 列出最低薪金大于1500的各种工作及从事此工作的全部雇员人数
-- 第二十题 : 列出最低薪金大于1500的各种工作及从事此工作的全部雇员人数
-- step1 : 直接按照job进行分组然后having过滤就好了
SELECTjob,min( sal ),count(*)
FROMemp
GROUP BYjob
HAVINGmin( sal ) > 1500;
执行结果如下