leetcode SQL 错题集

20200329

问题1

给定一个 salary 表,如下所示,有 m = 男性 和 f = 女性 的值。交换所有的 f 和 m 值(例如,将所有 f 值更改为 m,反之亦然)。要求只使用一个更新(Update)语句,并且没有中间的临时表。

注意,您必只能写一个 Update 语句,请不要编写任何 Select 语句。

答案

update salary set sex = IF(sex = 'm', 'f', 'm')

知识点

可以使用 if 函数判断 列的值 进行数据交换 当没有where 条件的时候会更新全表
IF 函数用法
IF(expr,v1,v2) 如果表达式 expr 成立,返回结果 v1;否则,返回结果 v2

问题2

编写一个 SQL 查询,获取 Employee 表中第二高的薪水(Salary)。

答案

方法1:将 Salary 去重后降序排列,再返回第二条记录可得第二大的值
也许只有一个 Salary 值,将返回 null

select (select distinct Salary from Employee limit 1,1) as secondhighestsalary;

方法2:找出小于该字段最大值的最大值,即为第二大值

select MAX(Salary) as secondhighestsalary from Employee  
where Salary <(select MAX(Salary) from Employee );

知识点

limit N     # 返回 N 条记录
offset M    # 跳过 M 条记录,M 默认为 0
limit M,N   # 相当于 limit N offset M,从第 M 条记录开始,返回 N 条记录

20200412

问题3

查找薪水涨幅超过15次的员工号emp_no以及其对应的涨幅次数t

CREATE TABLE salaries (
emp_no int(11) NOT NULL,
salary int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,from_date));

答案:

select 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变少理解为涨幅为负

疑惑:

此题应该是默认每次变化都是增长且不重复的。

问题4

找出所有员工当前(to_date=‘9999-01-01’)具体的薪水salary情况,对于相同的薪水只显示一次,并按照逆序显示

CREATE TABLE salaries (
emp_no int(11) NOT NULL,
salary int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,from_date));

答案:

方法1:适用小表

select distinct salary from salaries 
where to_date='9999-01-01' order by salary desc

方法2:适用大表

select  salary from salaries 
where to_date='9999-01-01' group by salary
order by salary desc

知识点

1.大表一般用distinct效率不高,大数据量的时候都禁止用distinct,建议用group by解决重复问题
2.WHERE语句在GROUP BY语句之前,SQL会在分组之前计算WHERE语句。
HAVING语句在GROUP BY语句之后,SQL会在分组之后计算HAVING语句
having 是过滤组 where过滤行

不足:

需增强order by 语句

问题5

获取所有部门当前manager的当前薪水情况,给出dept_no, emp_no以及salary,当前表示to_date=‘9999-01-01’
CREATE TABLE dept_manager (
dept_no char(4) NOT NULL,
emp_no int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,dept_no));
CREATE TABLE salaries (
emp_no int(11) NOT NULL,
salary int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,from_date));

答案

select d.dept_no,d.emp_no,s.salary 
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'

错误解法

select dept_no,emp_no,salary from dept_manager 
where to_date='9999-01-01' 
from salaries inner join dept_manager
on salaries.emp_no = dept_manager.emp_no

解析:

先联结再where

问题6

获取所有非manager的员工emp_no

CREATE TABLE dept_manager (
dept_no char(4) NOT NULL,
emp_no int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,dept_no));
CREATE TABLE employees (
emp_no int(11) NOT NULL,
birth_date date NOT NULL,
first_name varchar(14) NOT NULL,
last_name varchar(16) NOT NULL,
gender char(1) NOT NULL,
hire_date date NOT NULL,
PRIMARY KEY (emp_no));

答案:

方法一:先使用LEFT JOIN连接两张表,再从此表中选出dept_no值为NULL对应的emp_no记录

select employees.emp_no from employees
left join dept_manager
on employees.emp_no = dept_manager.emp_no
where dept_no is null;

方法二:使用NOT IN选出在employees但不在dept_manager中的emp_no记录

select emp_no from employees
where emp_no not in (
select emp_no from dept_manager)

不足

寻找空值的语句为:where dept_no is null

问题7

获取所有员工当前的manager,如果当前的manager是自己的话结果不显示,当前表示to_date=‘9999-01-01’。

结果第一列给出当前员工的emp_no,第二列给出其manager对应的manager_no。

CREATE TABLE dept_emp (
emp_no int(11) NOT NULL,
dept_no char(4) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,dept_no));
CREATE TABLE dept_manager (
dept_no char(4) NOT NULL,
emp_no int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,dept_no));

答案

select de.emp_no,dm.emp_no AS manager_no 
from dept_manager AS dm,dept_emp AS de
where de.emp_no <> dm.emp_no
and de.dept_no = dm.dept_no
and dm.to_date='9999-01-01';

解析

1.因为要输出自己的经理,得知自己与经理的部门要相同,故有限制条件 de.dept_no = dm.dept_no
2.select… from …where…可以直接连接表
3.为了增强代码可读性,将 dept_emp 用别名 de 代替,dept_manager 用 dm 代替

问题8

对应的salary

CREATE TABLE dept_emp (
emp_no int(11) NOT NULL,
dept_no char(4) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,dept_no));
CREATE TABLE salaries (
emp_no int(11) NOT NULL,
salary int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,from_date));

答案

select d.dept_no, s.emp_no, max(s.salary)
from dept_emp as d,salaries as s
where d.emp_no = s.emp_no
and d.to_date = '9999-01-01' 
and s.to_date = '9999-01-01' 
group by dept_no

解析

1.先用INNER JOIN连接两张表,限制条件是两张表的emp_no相同,即d.emp_no = s.emp_no;
2.用GROUP BY d.dept_no将每个部门分为一组,用MAX()函数选取每组中工资最高者

20200413

问题9

题目描述

从titles表获取按照title进行分组,每组个数大于等于2,给出title以及对应的数目t。
注意对于重复的emp_no进行忽略。
CREATE TABLE IF NOT EXISTS titles (
emp_no int(11) NOT NULL,
title varchar(50) NOT NULL,
from_date date NOT NULL,
to_date date DEFAULT NULL);

答案

方法一

select title ,count(distinct emp_no) as t
from titles
group by title
having t>=2

方法二

select title,count(title) as tfrom (select distinct emp_no,title,from_date,to_date     from titles )group by title having t>=2;

疑惑:

本题中,title的数=emp_no=记录数吗?

问题10

题目描述

查找employees表所有emp_no为奇数,且last_name不为Mary的员工信息,并按照hire_date逆序排列

CREATE TABLE employees (
emp_no int(11) NOT NULL,
birth_date date NOT NULL,
first_name varchar(14) NOT NULL,
last_name varchar(16) NOT NULL,
gender char(1) NOT NULL,
hire_date date NOT NULL,
PRIMARY KEY (emp_no))

答案

select * from employees
where emp_no % 2 =1
and last_name != 'Mary'
order by hire_date desc

解析:

三点需要注意:
1、员工号为奇数,则emp_no取余(%)应为1
2、last_name不为Mary,Mary 要用’’,用‘!=’或‘<>’表示
3. 根据hire_date逆序排列,用desc

问题11

题目描述

统计出当前各个title类型对应的员工当前(to_date=‘9999-01-01’)薪水对应的平均工资。结果给出title以及平均工资avg。
CREATE TABLE salaries (
emp_no int(11) NOT NULL,
salary int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,from_date));
CREATE TABLE IF NOT EXISTS “titles” (
emp_no int(11) NOT NULL,
title varchar(50) NOT NULL,
from_date date NOT NULL,
to_date date DEFAULT NULL);

答案

select title ,avg(salary) as avg
from titles a inner join salaries b
on a.emp_no= b.emp_no and a.to_date= '9999-01-01'  and b.to_date = '9999-01-01'
group by a.title 

20200426

问题12

查找所有员工的last_name和first_name以及对应的dept_name,也包括暂时没有分配部门的员工
CREATE TABLE departments (
dept_no char(4) NOT NULL,
dept_name varchar(40) NOT NULL,
PRIMARY KEY (dept_no));
CREATE TABLE dept_emp (
emp_no int(11) NOT NULL,
dept_no char(4) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,dept_no));
CREATE TABLE employees (
emp_no int(11) NOT NULL,
birth_date date NOT NULL,

答案:

SELECT last_name,first_name,de.dept_name
from employees as e
left join dept_emp as d on e.emp_no =d.emp_no
left join departments as de on d.dept_no =de.dept_no

解析

本题思路为运用两次LEFT JOIN连接嵌套
1、第一次LEFT JOIN连接employees表与dept_emp表,得到所有员工的last_name和first_name以及对应的dept_no,也包括暂时没有分配部门的员工
2、第二次LEFT JOIN连接上表与departments表,即连接dept_no与dept_name,得到所有员工的last_name和first_name以及对应的dept_name,也包括暂时没有分配部门的员工

问题13

查找员工编号emp_no为10001其自入职以来的薪水salary涨幅值growth
CREATE TABLE salaries (
emp_no int(11) NOT NULL,
salary int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,from_date));

答案

select 
( select salary from salaries  where emp_no = 10001
order by to_date desc limit 1)
-
( select salary from salaries  where emp_no = 10001
order by to_date asc limit 1) as growth

解析

1、先分别找到emp_no=10001的员工的第一次工资记录与最后一次工资记录
2、再将最后一次工资记录减去第一次工资记录得到入职以来salary的涨幅,最后用别名growth代替

问题14

查找所有员工自入职以来的薪水涨幅情况,给出员工编号emp_no以及其对应的薪水涨幅growth,并按照growth进行升序
CREATE TABLE employees (
emp_no int(11) NOT NULL,
birth_date date NOT NULL,
first_name varchar(14) NOT NULL,
last_name varchar(16) NOT NULL,
gender char(1) NOT NULL,
hire_date date NOT NULL,
PRIMARY KEY (emp_no));
CREATE TABLE salaries (
emp_no int(11) NOT NULL,
salary int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,from_date));

答案

select a.emp_no, (b.salary - c.salary) as growth
from
    employees as a
    inner join salaries as b
    on a.emp_no=b.emp_no and b.to_date ='9999-01-01'
    inner join salaries  as c
    on a.emp_no=c.emp_no  where  a.hire_date = c.from_date
order by growth asc

解析

1.同个表可重复联结,选定不同的日期相减
2.注意on后的条件用“and”连接而不是“where”

问题15

统计各个部门的工资记录数,给出部门编码dept_no、部门名称dept_name以及次数sum
CREATE TABLE departments (
dept_no char(4) NOT NULL,
dept_name varchar(40) NOT NULL,
PRIMARY KEY (dept_no));
CREATE TABLE dept_emp (
emp_no int(11) NOT NULL,
dept_no char(4) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,dept_no));
CREATE TABLE salaries (
emp_no int(11) NOT NULL,
salary int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,from_date));

答案

select de.dept_no,de.dept_name, count(s.salary) as sum
from departments as de ,dept_emp as d ,salaries as s
where de.dept_no =d.dept_no
and  d.emp_no = s.emp_no
group by  d.dept_no

疑惑

此处为什么用全连接

20200428

问题16

题目描述

对所有员工的当前(to_date=‘9999-01-01’)薪水按照salary进行按照1-N的排名,相同salary并列且按照emp_no升序排列

CREATE TABLE salaries (
emp_no int(11) NOT NULL,
salary int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,from_date));

答案

select s1.emp_no,s1.salary ,count( distinct s2.salary) as rank
from salaries as s1,salaries as s2
WHERE s1.to_date='9999-01-01'
and s2.to_date='9999-01-01'
and s1.salary<= s2.salary 
group by s1.emp_no
order by s1.salary desc,s1.emp_no asc

解析

本题的主要思想是复用salaries表进行比较排名,具体思路如下:
1、从两张相同的salaries表(分别为s1与s2)进行对比分析,先将两表限定条件设为to_date = ‘9999-01-01’,挑选出当前所有员工的薪水情况。
2、本题的精髓在于 s1.salary <= s2.salary,意思是在输出s1.salary的情况下,有多少个s2.salary大于等于1.salary,比如当s1.salary=94409时,有3个s2.salary(分别为94692,94409,94409)大于等于它,但由于94409重复,利用COUNT(DISTINCT s2.salary)去重可得工资为94409的rank等于2。其余排名以此类推。
3、千万不要忘了GROUP BY s1.emp_no,否则输出的记录只有一条(可能是第一条或者最后一条,根据不同的数据库而定),因为用了合计函数COUNT()
4、最后先以 s1.salary 逆序排列,再以 s1.emp_no 顺序排列输出结果

问题17

获取所有非manager员工当前的薪水情况,给出dept_no、emp_no以及salary ,当前表示to_date=‘9999-01-01’

CREATE TABLE dept_emp (
emp_no int(11) NOT NULL,
dept_no char(4) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,dept_no));
CREATE TABLE dept_manager (
dept_no char(4) NOT NULL,
emp_no int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,dept_no));
CREATE TABLE employees (
emp_no int(11) NOT NULL,
birth_date date NOT NULL,
first_name varchar(14) NOT NULL,
last_name varchar(16) NOT NULL,
gender char(1) NOT NULL,
hire_date date NOT NULL,
PRIMARY KEY (emp_no));
CREATE TABLE salaries (
emp_no int(11) NOT NULL,
salary int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,from_date));

答案

select de.dept_no,s.emp_no,s.salary
from employees as e
inner join salaries as s on e.emp_no = s.emp_no and s.to_date='9999-01-01'
inner join dept_emp as de on e.emp_no = de.emp_no 
where de.emp_no not in (
select emp_no from dept_manager where to_date='9999-01-01')

解析

1、先用INNER JOIN连接employees和salaries,找出当前所有员工的工资情况
2、再用INNER JOIN连接dept_emp表,找到所有员工所在的部门
3、最后用限制条件de.emp_no NOT IN (SELECT emp_no FROM dept_manager WHERE to_date = ‘9999-01-01’)选出当前所有非manager员工,再依次输出dept_no、emp_no、salary

疑惑

1.on 后面可以加and还是where?
2.主键的连接,如何判断连接一个还是两个?

20200517

(从这里开始觉得好难,没思路,哭晕在厕所,但素!坚持住~)

问题18

获取员工其当前的薪水比其manager当前薪水还高的相关信息,当前表示to_date=‘9999-01-01’,
结果第一列给出员工的emp_no,
第二列给出其manager的manager_no,
第三列给出该员工当前的薪水emp_salary,
第四列给该员工对应的manager当前的薪水manager_salary
CREATE TABLE dept_emp (
emp_no int(11) NOT NULL,
dept_no char(4) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,dept_no));
CREATE TABLE dept_manager (
dept_no char(4) NOT NULL,
emp_no int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,dept_no));
CREATE TABLE salaries (
emp_no int(11) NOT NULL,
salary int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,from_date));

思路:

1.把问题拆分成小表,获取小表后再进一步提取得到大表,由主键找表的连接方式。
2.“获取员工其当前的薪水比其manager当前薪水还高”需要两个薪水表:员工和经理
因此:
(1)先用INNER JOIN连接salaries和demp_emp,建立员工的工资记录
(2)再用INNER JOIN连接salaries和demp_manager,建立经理的工资记录
3. 最后用限制条件s1.dept_no = s2.dept_no AND s1.salary > s2.salary找出同一部门中工资比经理高的员工,并根据题意依次输出emp_no、manager_no、emp_salary、manager_salary

答案

select s1.emp_no as emp_no,s2.emp_no as manager_no,s1.salary as emp_salary,s2.salary as manager_salary
from(
select s.emp_no,s.salary,de.dept_no
from dept_emp  as de inner join salaries as s
on de.emp_no = s.emp_no  
and s.to_date='9999-01-01' ) as s1,
(select s.emp_no,s.salary,dm.dept_no
from dept_manager as dm inner join salaries as s
on dm.emp_no = s.emp_no 
and s.to_date='9999-01-01' ) as s2
where  s1.dept_no =s2.dept_no
and s1.salary > s2.salary

20200523

问题19

汇总各个部门当前员工的title类型的分配数目,结果给出部门编号dept_no、dept_name、其当前员工所有的title以及该类型title对应的数目count
CREATE TABLE departments (
dept_no char(4) NOT NULL,
dept_name varchar(40) NOT NULL,
PRIMARY KEY (dept_no));
CREATE TABLE dept_emp (
emp_no int(11) NOT NULL,
dept_no char(4) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,dept_no));
CREATE TABLE IF NOT EXISTS titles (
emp_no int(11) NOT NULL,
title varchar(50) NOT NULL,
from_date date NOT NULL,
to_date date DEFAULT NULL);

答案


select de.dept_no ,de.dept_name ,
t.title ,count(t.title) as count 
from dept_emp em inner join departments de
on de.dept_no = em.dept_no
and em.to_date='9999-01-01'
inner join titles t
on em.emp_no = t.emp_no
and t.to_date='9999-01-01'
group by de.dept_no,t.title

解析

本题的关键在于用 GROUP BY 同时对 de.dept_no 和 t.title 进行分组,具体思路如下:
1、先用 INNER JOIN 连接 dept_emp 与 salaries,根据测试数据添加限定条件 de.to_date = ‘9999-01-01’ AND t.to_date = ‘9999-01-01’,即当前员工的当前头衔
2、再用 INNER JOIN 连接departments,限定条件为 de.dept_no = dp.dept_no,即部门编号相同
3、最后用 GROUP BY 同时对 de.dept_no 和 t.title 进行分组,用 COUNT(t.title) 统计相同部门下相同头衔的员工个数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值