|
| 1 | +## 数据分析师SQL面试宝典 |
| 2 | + |
| 3 | +### 题目1 |
| 4 | + |
| 5 | +MySQL 数据库中有如下所示的两张表部门表和员工表,请完成下面的查询。 |
| 6 | + |
| 7 | +部门表(`tb_dept`):三个字段分别代表部门编号(`dno`)、部门名称(`dname`)和部门所在地(`dloc`)。 |
| 8 | + |
| 9 | +| dno | dname | dloc | |
| 10 | +| :--: | :----: | :--: | |
| 11 | +| 10 | 会计部 | 北京 | |
| 12 | +| 20 | 研发部 | 成都 | |
| 13 | +| 30 | 销售部 | 重庆 | |
| 14 | +| 40 | 运维部 | 深圳 | |
| 15 | + |
| 16 | +员工表(`tb_emp`):六个字段分别代表员工编号(`eno`)、姓名(`ename`)、职位(`job`)、主管编号(`mgr`)、月薪(`sal`)、所在部门编号(`dno`),其中主管编号参照了员工表的员工编号,所在部门编号参照了部门表的部门编号。 |
| 17 | + |
| 18 | +| eno | ename | job | mgr | sal | dno | |
| 19 | +| :--: | :----: | :------: | :--: | :--: | :--: | |
| 20 | +| 1359 | 胡一刀 | 销售员 | 3344 | 1800 | 30 | |
| 21 | +| 2056 | 乔峰 | 分析师 | 7800 | 5000 | 20 | |
| 22 | +| 3088 | 李莫愁 | 设计师 | 2056 | 3500 | 20 | |
| 23 | +| 3211 | 张无忌 | 程序员 | 2056 | 3200 | 20 | |
| 24 | +| 3233 | 丘处机 | 程序员 | 2056 | 3400 | 20 | |
| 25 | +| 3244 | 欧阳锋 | 程序员 | 3088 | 3200 | 20 | |
| 26 | +| 3251 | 张翠山 | 程序员 | 2056 | 4000 | 20 | |
| 27 | +| 3344 | 黄蓉 | 销售主管 | 7800 | 3000 | 30 | |
| 28 | +| 3577 | 杨过 | 会计 | 5566 | 2200 | 10 | |
| 29 | +| 3588 | 朱九真 | 会计 | 5566 | 2500 | 10 | |
| 30 | +| 4466 | 苗人凤 | 销售员 | 3344 | 2500 | 30 | |
| 31 | +| 5234 | 郭靖 | 出纳 | 5566 | 2000 | 10 | |
| 32 | +| 5566 | 宋远桥 | 会计师 | 7800 | 4000 | 10 | |
| 33 | +| 7800 | 张三丰 | 总裁 | NULL | 9000 | 20 | |
| 34 | + |
| 35 | +#### 1. 查询员工和他的主管的姓名 |
| 36 | + |
| 37 | +参考答案: |
| 38 | + |
| 39 | +```sql |
| 40 | +SELECT t1.ename AS 员工姓名 |
| 41 | + , t2.ename AS 主管姓名 |
| 42 | + FROM tb_emp AS t1 |
| 43 | + LEFT JOIN tb_emp AS t2 |
| 44 | + ON t1.mgr = t2.eno; |
| 45 | +``` |
| 46 | + |
| 47 | +> **说明**:本题通过自连接就可以完成,使用左外连接是为了将没有主管的总裁张三丰也能查出来。 |
| 48 | +
|
| 49 | +#### 2. 查询月薪最高的员工姓名和月薪 |
| 50 | + |
| 51 | +参考答案: |
| 52 | + |
| 53 | +方法一:嵌套查询(先查出最高的月薪再用它当条件筛选出对应的员工) |
| 54 | + |
| 55 | +```sql |
| 56 | +SELECT ename |
| 57 | + , sal |
| 58 | + FROM tb_emp |
| 59 | + WHERE sal = (SELECT MAX(sal) |
| 60 | + FROM tb_emp); |
| 61 | +``` |
| 62 | + |
| 63 | +方法二:ALL运算符(拿你的月薪和所有人比较,看看是否满足条件`>=`) |
| 64 | + |
| 65 | +```sql |
| 66 | +SELECT ename |
| 67 | + , sal |
| 68 | + FROM tb_emp |
| 69 | + WHERE sal >= ALL(SELECT sal |
| 70 | + FROM tb_emp); |
| 71 | +``` |
| 72 | + |
| 73 | +方法三:计数法(月薪比你更高的员工人数为`0`,你就是月薪最高的) |
| 74 | + |
| 75 | +```sql |
| 76 | +SELECT ename |
| 77 | + , sal |
| 78 | + FROM tb_emp AS t1 |
| 79 | + WHERE (SELECT COUNT(*) |
| 80 | + FROM tb_emp AS t2 |
| 81 | + WHERE t2.sal > t1.sal) = 0; |
| 82 | +``` |
| 83 | + |
| 84 | +方法四:存在性判断(不存在有人比你月薪更高,那你就是月薪最高的) |
| 85 | + |
| 86 | +```sql |
| 87 | +SELECT ename |
| 88 | + , sal |
| 89 | + FROM tb_emp AS t1 |
| 90 | + WHERE NOT EXISTS (SELECT 'x' |
| 91 | + FROM tb_emp AS t2 |
| 92 | + WHERE t2.sal > t1.sal); |
| 93 | +``` |
| 94 | + |
| 95 | +> **说明**:存在性判断并不需要投影某个具体的字段,所以通常直接写常量`'x'`或`1`来代替具体的字段。 |
| 96 | +
|
| 97 | +#### 3. 查询月薪Top3的员工姓名和月薪 |
| 98 | + |
| 99 | +参考答案: |
| 100 | + |
| 101 | +```sql |
| 102 | +SELECT ename |
| 103 | + , sal |
| 104 | + FROM tb_emp AS t1 |
| 105 | + WHERE (SELECT COUNT(*) |
| 106 | + FROM tb_emp AS t2 |
| 107 | + WHERE t2.sal > t1.sal) < 3 |
| 108 | + ORDER BY sal DESC; |
| 109 | +``` |
| 110 | + |
| 111 | +> **说明**:这个查询跟上一个查询的方法三道理是一样的,如果月薪比我更高的人不会有3个,那么我们就是Top3。 |
| 112 | +
|
| 113 | +#### 4. 查询部门人数超过5个人的部门的编号和人数 |
| 114 | + |
| 115 | +参考答案: |
| 116 | + |
| 117 | +```sql |
| 118 | +SELECT dno AS 部门编号 |
| 119 | + , COUNT(*) AS 人数 |
| 120 | + FROM tb_emp |
| 121 | + GROUP BY dno |
| 122 | +HAVING COUNT(*) > 5; |
| 123 | +``` |
| 124 | + |
| 125 | +> **说明**:分组之前的数据筛选使用`WHERE`子句,分组之后的数据筛选要使用`HAVING`子句。 |
| 126 | +
|
| 127 | +#### 5. 查询所有部门的名称和人数 |
| 128 | + |
| 129 | +参考答案: |
| 130 | + |
| 131 | +```sql |
| 132 | +SELECT dname AS 部门名称 |
| 133 | + , COALESCE(total, 0) AS 部门人数 |
| 134 | + FROM tb_dept AS t1 |
| 135 | + LEFT JOIN (SELECT dno |
| 136 | + , COUNT(*) AS total |
| 137 | + FROM tb_emp |
| 138 | + GROUP BY dno) AS t2 |
| 139 | + ON t1.dno = t2.dno; |
| 140 | +``` |
| 141 | + |
| 142 | +> **说明**:先通过一个嵌套查询获取到部门编号和人数,再通过左外连接的方式连接部门表。 |
| 143 | +
|
| 144 | +#### 4. 查询月薪超过其所在部门平均月薪的员工的姓名、部门编号和月薪 |
| 145 | + |
| 146 | +参考答案: |
| 147 | + |
| 148 | +```sql |
| 149 | +SELECT ename |
| 150 | + , dno |
| 151 | + , sal |
| 152 | + FROM tb_emp AS t1 |
| 153 | + NATURAL JOIN (SELECT dno |
| 154 | + , AVG(sal) AS avg_sal |
| 155 | + FROM tb_emp |
| 156 | + GROUP BY dno) AS t2 |
| 157 | + WHERE sal > avg_sal; |
| 158 | +``` |
| 159 | + |
| 160 | +#### 5. 查询部门中月薪最高的人姓名、月薪和所在部门名称 |
| 161 | + |
| 162 | +参考答案: |
| 163 | + |
| 164 | +```sql |
| 165 | +SELECT ename |
| 166 | + , sal |
| 167 | + , dname |
| 168 | + FROM tb_dept AS t1 |
| 169 | + NATURAL JOIN tb_emp AS t2 |
| 170 | + WHERE (dno, sal) IN (SELECT dno |
| 171 | + , max(sal) |
| 172 | + FROM tb_emp |
| 173 | + GROUP BY dno); |
| 174 | +``` |
| 175 | + |
| 176 | +#### 6. 查询主管和普通员工的平均月薪 |
| 177 | + |
| 178 | +参考答案: |
| 179 | + |
| 180 | +```sql |
| 181 | +SELECT tag AS 职级 |
| 182 | + , ROUND(avg(sal), 2) AS 平均月薪 |
| 183 | + FROM (SELECT sal |
| 184 | + , CASE WHEN EXISTS (SELECT 'x' |
| 185 | + FROM tb_emp AS t2 |
| 186 | + WHERE t1.eno = t2.mgr) |
| 187 | + THEN '主管' |
| 188 | + ELSE '普通员工' |
| 189 | + END AS tag |
| 190 | + FROM tb_emp AS t1) AS tmp |
| 191 | + GROUP BY tag; |
| 192 | +``` |
| 193 | + |
| 194 | +> **说明**:先通过一个嵌套查询,给原来员工表的数据加上“主管”或“普通员工”的标签,然后根据标签分组数据再做聚合。 |
| 195 | +
|
| 196 | +#### 7. 查询月薪排名4~6名的员工排名、姓名和月薪 |
| 197 | + |
| 198 | +参考答案: |
| 199 | + |
| 200 | +方法一:不使用窗口函数 |
| 201 | + |
| 202 | +```sql |
| 203 | +SELECT * |
| 204 | + FROM (SELECT @a := @a + 1 AS 排名 |
| 205 | + , ename AS 姓名 |
| 206 | + , sal AS 月薪 |
| 207 | + FROM tb_emp, (SELECT @a := 0) AS tmp |
| 208 | + ORDER BY sal DESC) AS tmp |
| 209 | + WHERE 排名 BETWEEN 4 AND 6; |
| 210 | +``` |
| 211 | + |
| 212 | +方法二:使用窗口函数 |
| 213 | + |
| 214 | +```sql |
| 215 | +SELECT * |
| 216 | + FROM (SELECT ename AS 姓名 |
| 217 | + , sal AS 月薪 |
| 218 | + , RANK() OVER (ORDER BY sal DESC) AS 排名 |
| 219 | + FROM tb_emp) AS tmp |
| 220 | + WHERE 排名 between 4 and 6; |
| 221 | +``` |
| 222 | + |
| 223 | +> **说明**:上面方法一和方法二的查询结果并不相同,方法二的查询结果更符合我们对这个问题的认知,如果要获得跟方法一一样的结果,可以将窗口函数`RANK`修改为`ROW_NUMBER`即可。`RANK`、`DENSE_RANK`、`ROW_NUMBER`三个函数的差别大家可以自行了解。 |
| 224 | +
|
| 225 | +#### 8. 查询每个部门月薪排前2名的员工姓名、月薪和部门编号 |
| 226 | + |
| 227 | +参考答案: |
| 228 | + |
| 229 | +方法一:不使用窗口函数 |
| 230 | + |
| 231 | +```sql |
| 232 | +SELECT ename |
| 233 | + , sal |
| 234 | + , t1.dno |
| 235 | + FROM tb_emp AS t1 |
| 236 | + WHERE (SELECT COUNT(*) |
| 237 | + FROM tb_emp AS t2 |
| 238 | + WHERE t2.dno = t1.dno |
| 239 | + AND t2.sal > t1.sal) < 2 |
| 240 | + ORDER BY dno ASC, sal DESC; |
| 241 | +``` |
| 242 | + |
| 243 | +方法二:使用窗口函数 |
| 244 | + |
| 245 | +```sql |
| 246 | +SELECT ename |
| 247 | + , sal |
| 248 | + , dno |
| 249 | + FROM (SELECT ename |
| 250 | + , sal |
| 251 | + , dno |
| 252 | + , RANK() OVER (PARTITION BY dno ORDER BY sal DESC) AS rn |
| 253 | + FROM tb_emp) AS tmp |
| 254 | + WHERE rn <= 2; |
| 255 | +``` |
| 256 | + |
| 257 | +> **说明**:上面的题目总体难度不大,但是覆盖到的 SQL 知识点还是比较多的,大家可以用这些题来热热身,再去挑战后面的 SQL 面试题,这些面试题主要是针对数据分析师的 SQL 面试题。如果对 SQL 的知识不太熟悉,大家可以先移步到我在B站上发布的视频[《数据分析师的SQL入门课》](https://www.bilibili.com/video/BV13V4y1H7mT)进行学习。下面是为大家编写好的建库建表和插入数据的SQL语句,有需要的可以用它来完成建库建表操作。 |
| 258 | +> |
| 259 | +> ```SQL |
| 260 | +> -- 创建数据库 |
| 261 | +> CREATE DATABASE `Q1` DEFAULT CHARSET utf8mb4; |
| 262 | +> |
| 263 | +> -- 切换数据库 |
| 264 | +> USE `Q1`; |
| 265 | +> |
| 266 | +> -- 创建部门表 |
| 267 | +> CREATE TABLE `tb_dept` |
| 268 | +> ( |
| 269 | +> `dno` int NOT NULL COMMENT '编号', |
| 270 | +> `dname` varchar(10) NOT NULL COMMENT '名称', |
| 271 | +> `dloc` varchar(20) NOT NULL COMMENT '所在地', |
| 272 | +> PRIMARY KEY (`dno`) |
| 273 | +> ); |
| 274 | +> |
| 275 | +> -- 插入部门数据 |
| 276 | +> INSERT INTO `tb_dept` |
| 277 | +> VALUES |
| 278 | +> (10, '会计部', '北京'), |
| 279 | +> (20, '研发部', '成都'), |
| 280 | +> (30, '销售部', '重庆'), |
| 281 | +> (40, '运维部', '深圳'); |
| 282 | +> |
| 283 | +> -- 创建员工表 |
| 284 | +> CREATE TABLE `tb_emp` |
| 285 | +> ( |
| 286 | +> `eno` int NOT NULL COMMENT '员工编号', |
| 287 | +> `ename` varchar(20) NOT NULL COMMENT '员工姓名', |
| 288 | +> `job` varchar(20) NOT NULL COMMENT '员工职位', |
| 289 | +> `mgr` int COMMENT '主管编号', |
| 290 | +> `sal` int NOT NULL COMMENT '员工月薪', |
| 291 | +> `dno` int NOT NULL COMMENT '所在部门编号', |
| 292 | +> PRIMARY KEY (`eno`), |
| 293 | +> CONSTRAINT `fk_emp_mgr` FOREIGN KEY (`mgr`) REFERENCES tb_emp (`eno`), |
| 294 | +> CONSTRAINT `fk_emp_dno` FOREIGN KEY (`dno`) REFERENCES tb_dept (`dno`) |
| 295 | +> ); |
| 296 | +> |
| 297 | +> -- 插入员工数据 |
| 298 | +> INSERT INTO `tb_emp` |
| 299 | +> VALUES |
| 300 | +> (7800, '张三丰', '总裁', NULL, 9000, 20), |
| 301 | +> (2056, '乔峰', '分析师', 7800, 5000, 20), |
| 302 | +> (3088, '李莫愁', '设计师', 2056, 3500, 20), |
| 303 | +> (3211, '张无忌', '程序员', 2056, 3200, 20), |
| 304 | +> (3233, '丘处机', '程序员', 2056, 3400, 20), |
| 305 | +> (3251, '张翠山', '程序员', 2056, 4000, 20), |
| 306 | +> (5566, '宋远桥', '会计师', 7800, 4000, 10), |
| 307 | +> (5234, '郭靖', '出纳', 5566, 2000, 10), |
| 308 | +> (3344, '黄蓉', '销售主管', 7800, 3000, 30), |
| 309 | +> (1359, '胡一刀', '销售员', 3344, 1800, 30), |
| 310 | +> (4466, '苗人凤', '销售员', 3344, 2500, 30), |
| 311 | +> (3244, '欧阳锋', '程序员', 3088, 3200, 20), |
| 312 | +> (3577, '杨过', '会计', 5566, 2200, 10), |
| 313 | +> (3588, '朱九真', '会计', 5566, 2500, 10); |
| 314 | +> ``` |
| 315 | +
|
| 316 | + |
| 317 | +
|
0 commit comments