来源于:牛客网
1.sql查找表中多余的重复记录,重复记录是根据单个字段(itemId)来判断:
答:1
2
3select * from tb_Item
where itemId in (select itemId from tb_Item
group by itemId having count(itemId) > 1 )
2.1 查找最晚入职员工的所有信息:
1 | CREATE TABLE `employees` ( |
答:1
2
3select * from employees
where hire_date =
(select max(hire_date) from employees)
2.2 查找入职员工时间排名倒数第三的员工所有信息(接上题)
答:对入职时间进行降序排列,从第2条记录开始,选取1条记录信息。1
SELECT * FROM employees ORDER BY hire_date DESC LIMIT 2,1
3.查找各个部门当前(to_date=’9999-01-01’)领导当前薪水详情以及其对应部门编号dept_no
1 | CREATE TABLE `dept_manager` ( |
1 | CREATE TABLE `salaries` ( |
答:(注意这里两个表的日期要同步)
解1:1
2
3
4
5select s.*, d.dept_no
from salaries s, dept_manager d
where s.emp_no = d.emp_no
and s.to_date = '9999-01-01'
and d.to_date = '9999-01-01'
解2:(使用关联查询)1
2
3
4
5
6select s.*, d.dept_no
from salaries as s
inner join dept_manager as d
on s.emp_no = d.emp_no
and s.to_date = '9999-01-01'
and d.to_date = '9999-01-01'
解3:(只是加了一个where)1
2
3
4
5
6select s.*, d.dept_no
from salaries s
inner join dept_manager d
on s.emp_no = d.emp_no
where s.to_date = '9999-01-01'
and d.to_date = '9999-01-01'
4.1 查找所有已经分配部门的员工的last_name和first_name
1 | CREATE TABLE `dept_emp` ( |
1 | CREATE TABLE `employees` ( |
答:
解1:1
2
3
4select e.last_name, e.first_name, d.dept_no
from employees e
inner join dept_emp d
on e.emp_no = d.emp_no
解2:(//只有一列是公有的,用自然连接)1
2
3select e.last_name, e.first_name, d.dept_no
from employees e
natural join dept_emp d
4.2 查找所有员工的last_name和first_name以及对应部门编号dept_no,也包括展示没有分配具体部门的员工(接上题)
答:1
2
3
4SELECT e.last_name, e.first_name, d.dept_no
FROM employees e
LEFT JOIN dept_emp d
ON e.emp_no = d.emp_no
注意:
INNER JOIN 两边表同时有对应的数据,即任何一边缺失数据就不显示。
LEFT JOIN 会读取左边数据表的全部数据,即便右边表无对应数据。
RIGHT JOIN 会读取右边数据表的全部数据,即便左边表无对应数据。
4.3查找所有员工入职时候的薪水情况,给出emp_no以及salary, 并按照emp_no进行逆序(接上题)
答:
此题应注意以下四个知识点:
1.由于测试数据中,salaries.emp_no 不唯一(因为号码为 emp_no 的员工会有多次涨薪的可能,所以在 salaries 中对应的记录不止一条),employees.emp_no 唯一,即 salaries 的数据会多于 employees,因此需先找到 employees.emp_no 在 salaries 表中对应的记录salaries.emp_no,则有限制条件 e.emp_no = s.emp_no
2.根据题意注意到 salaries.from_date 和 employees.hire_date 的值应该要相等,因此有限制条件 e.hire_date = s.from_date
3.根据题意要按照 emp_no 值逆序排列,因此最后要加上 ORDER BY e.emp_no DESC
4.为了代码良好的可读性,运用了 Alias 别名语句,将 employees 简化为 e,salaries 简化为s,即 employees AS e 与 salaries AS s,其中 AS 可以省略
方法一:利用 INNER JOIN 连接两张表1
2
3SELECT e.emp_no, s.salary FROM employees AS e INNER JOIN salaries AS s
ON e.emp_no = s.emp_no AND e.hire_date = s.from_date
ORDER BY e.emp_no DESC
方法二:直接用逗号并列查询两张表1
2
3SELECT e.emp_no, s.salary FROM employees AS e, salaries AS s
WHERE e.emp_no = s.emp_no AND e.hire_date = s.from_date
ORDER BY e.emp_no DESC
5.查找薪水涨幅超过15次的员工号emp_no以及其对应的涨幅次数t:
1 | CREATE TABLE `salaries` ( |
答:1
2
3SELECT emp_no, COUNT(emp_no) AS t
FROM salaries
GROUP BY emp_no HAVING t > 15
解析:
此题应注意以下四点:1. 用COUNT()函数和GROUP BY语句可以统计同一emp_no值的记录条数
2. 根据题意,输出的涨幅次数为t,故用AS语句将COUNT(emp_no)的值转换为t
3. 由于COUNT()函数不可用于WHERE语句中,故使用HAVING语句来限定t>15的条件
4. 最后存在一个理解误区,涨幅超过15次,salaries中相应的记录数应该超过16(从第2条记录开始算作第1次涨幅),不过题目为了简单起见,将第1条记录当作第1次涨幅,所以令t>15即可
注意:严格来说,下一条salary高于本条才算涨幅,但本题只要出现了一条记录就算一次涨幅,salary相同可以理解为涨幅为0,salary变少理解为涨幅为负
6.找出所有员工当前(to_date=’9999-01-01’)具体的薪水salary情况,对于相同的薪水只显示一次,并按照逆序显示:
答:
方式一:1
2
3SELECT DISTINCT salary FROM salaries
WHERE to_date='9999-01-01'
ORDER BY salary DESC;
方式二:1
2
3
4SELECT salary FROM salaries
WHERE to_date='9999-01-01'
GROUP BY salary
ORDER BY salary DESC;
解析:
对于distinct,groupby的性能:1.数据量非常巨大时候,比如1000万中有300W重复数据,这时候的distinct的效率略好于group by;
2.对于相对重复量较小的数据量比如1000万中1万的重复量,用groupby的性能会远优于distnct;
3.简书上的一篇博客说的不错,大家可以穿送过去看一看
传送门
7.获取所有部门当前manager的当前薪水情况,给出dept_no, emp_no以及salary,当前表示to_date=’9999-01-01’:
1 | CREATE TABLE `dept_manager` ( |
1 | CREATE TABLE `salaries` ( |
答:
方式一:(where关联查询语句)1
2
3
4
5SELECT d.dept_no, d.emp_no, s.salary
FROM salaries AS s, dept_manager AS d
WHERE d.emp_no = s.emp_no
AND s.to_date = '9999-01-01'
AND d.to_date = '9999-01-01';
方式二:(内连接)1
2
3
4
5
6SELECT d.dept_no, d.emp_no, s.salary
FROM salaries AS s
INNER JOIN dept_manager AS d
ON d.emp_no = s.emp_no
WHERE d.to_date = '9999-01-01'
AND s.to_date = '9999-01-01'
或:1
2
3
4
5
6SELECT d.dept_no, d.emp_no, s.salary
FROM salaries AS s
INNER JOIN dept_manager AS d
ON d.emp_no = s.emp_no
AND d.to_date = '9999-01-01'
AND s.to_date = '9999-01-01'
8.获取所有非manager的员工emp_no:
1 | CREATE TABLE `dept_manager` ( |
1 | CREATE TABLE `employees` ( |
答:
方式一:(使用NOT IN选出在employees但不在dept_manager中的emp_no记录)1
2SELECT emp_no FROM employees
WHERE emp_no NOT IN (SELECT emp_no FROM dept_manager)
方式二:(先使用LEFT JOIN连接两张表,再从此表中选出dept_no值为NULL对应的emp_no记录)1
2
3
4SELECT e.emp_no FROM employees e
LEFT JOIN dept_manager d
ON e.emp_no = d.emp_no
WHERE dept_no IS NULL
9.获取所有员工当前的manager,如果当前的manager是自己的话结果不显示,当前表示to_date=’9999-01-01’。结果第一列给出当前员工的emp_no,第二列给出其manager对应的manager_no。
1 | CREATE TABLE `dept_emp` ( |
1 | CREATE TABLE `dept_manager` ( |
答:
方式一:(使用where条件语句)1
2
3
4
5
6SELECT de.emp_no, dm.emp_no AS manager_no
FROM dept_manager AS dm, dept_emp AS de
WHERE de.dept_no = dm.dept_no
AND de.to_date='9999-01-01'
AND dm.to_date='9999-01-01'
AND de.emp_no <> dm.emp_no
方式二:(使用inner join内连接)1
2
3
4
5
6SELECT de.emp_no, dm.emp_no AS manager_no
FROM dept_emp AS de INNER JOIN dept_manager AS dm
ON de.dept_no = dm.dept_no
WHERE dm.to_date = '9999-01-01'
AND de.to_date = '9999-01-01'
AND de.emp_no <> dm.emp_no
解析:
本题应注意以下三点:
1.用 INNER JOIN 连接两张表,因为要输出自己的经理,得知自己与经理的部门要相同,故有限制条件 de.dept_no = dm.dept_no
2.再用 WHERE 限制当前员工与当前经理的条件,即 dm.to_date 等于 ‘9999-01-01’ 、de.to_date 等于 ‘9999-01-01’ 、 de.emp_no 不等于 dm.emp_no
3.为了增强代码可读性,将 dept_emp 用别名 de 代替,dept_manager 用 dm 代替,最后根据题意将 de.emp_no 用别名 manager_no 代替后输出
10.获取所有部门中当前员工薪水最高的相关信息,给出dept_no, emp_no以及其对应的salary:
1 | CREATE TABLE `dept_emp` ( |
1 | CREATE TABLE `salaries` ( |
答:
方式一:1
2
3
4
5
6SELECT de.dept_no, de.emp_no, max(s.salary) salary
FROM dept_emp de,salaries s
WHERE de.emp_no = s.emp_no
AND de.to_date = '9999-01-01'
AND s.to_date = '9999-01-01'
GROUP BY de.dept_no;
方式二:1
2
3
4
5
6
7SELECT d.dept_no, s.emp_no, MAX(s.salary) AS salary
FROM salaries AS s
INNER JOIN dept_emp As d
ON d.emp_no = s.emp_no
WHERE d.to_date = '9999-01-01'
AND s.to_date = '9999-01-01'
GROUP BY d.dept_no