来自牛客SQL题库

有些个人理解的话可能不正确,毕竟纯小白上路,车不稳,有错误恳请大家批评指正。

题目1:

https://www.nowcoder.com/practice/218ae58dfdcd4af195fff264e062138f?tpId=82&tqId=29753&tPage=1&rp=&ru=%2Fta%2Fsql&qru=%2Fta%2Fsql%2Fquestion-ranking
题目描述 employees
查找最晚入职员工的所有信息

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`));

题解:一共5种写法(甚至更多),倒数的2种是最佳写法(此时同一天入职的看做是一样晚的)

/*按时间降序排列,并取第一个。 */
select * from employees 
    order by hire_date desc 
    limit 1;

/* 使用limit 与 offset关键字  */
select * from employees 
    order by hire_date desc 
    limit 1 offset 0;

/* 使用limit关键字 从第0条记录 向后读取一个,也就是第一条记录 */
select * from employees 
    order by hire_date desc 
    limit 0,1;


/* 使用子查询,最后一天的时间有多个员工信息 */
select * from employees
    where hire_date = (select max(hire_date) from employees);

/* 写法5:根据第2题的思路改写*/
SELECT *
FROM employees
WHERE hire_date = (
    SELECT DISTINCT hire_date
    FROM employees
    ORDER BY hire_date DESC       
); 

题目2

https://www.nowcoder.com/practice/ec1ca44c62c14ceb990c3c40def1ec6c?tpId=82&tqId=29754&tPage=1&rp=&ru=%2Fta%2Fsql&qru=%2Fta%2Fsql%2Fquestion-ranking
题目描述
查找入职员工时间排名倒数第三的员工所有信息

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`));

题解:第1和3种解法最佳,因为使用的去重(一天有多个员工入职,将这些同一天的员工视为并列第一)

/* 写法1:*/
SELECT * 
FROM employees
WHERE hire_date = (
    SELECT DISTINCT hire_date 
    FROM employees
    ORDER BY hire_date DESC       -- 倒序//--是注释
    LIMIT 1 OFFSET 2              -- 去掉排名倒数第一第二的时间,取倒数第三
);  

/* 写法2:*/
SELECT * FROM employees
ORDER BY hire_date DESC
LIMIT 1 offset 2;

/* 写法3:*/
select * from employees 
where hire_date = (
    select distinct hire_date 
    from employees 
    order by hire_date desc 
    limit 2,1          --从第2个记录往后读1个,就是第3个了。
)

第3题:

https://www.nowcoder.com/practice/c63c5b54d86e4c6d880e4834bfd70c3b?tpId=82&tqId=29755&tPage=1&rp=&ru=%2Fta%2Fsql&qru=%2Fta%2Fsql%2Fquestion-ranking
题目描述
查找各个部门当前(to_date='9999-01-01')领导当前薪水详情以及其对应部门编号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`));

图片说明

题解:第一个表中用部门编号dept_no,第二个表中用薪水salary。然后以salary表为主表,添加一列dept_no。所以select s.*,d.dept_no选取这么两个东西。emp_no是主键,连接后,以主键相同为标志。

/* 写法1:*/
select s.*,d.dept_no
from salaries as s  
inner join dept_manager as d
on s.emp_no=d.emp_no
where s.to_date='9999-01-01' and d.to_date='9999-01-01'

/* 写法2:*/
SELECT s.*, d.dept_no 
FROM salaries AS s ,  dept_manager AS d
WHERE s.to_date='9999-01-01'
AND d.to_date='9999-01-01'
AND s.emp_no=d.emp_no;

关于为什么一定要两个表格的时间都限制成规定时间(9999-01-01)呢?

  • 因为薪水表是按年发的,而题目要查找的是当前的薪水,所以要过滤掉以前(薪水表)。而dept_manager是因为有领导会离职,to_date时间不一定是9999-01-01,所以要过滤过离职的领导(经理表)。

题目4:

https://www.nowcoder.com/practice/6d35b1cd593545ab985a68cd86f28671?tpId=82&tqId=29756&tPage=1&rp=&ru=/ta/sql&qru=/ta/sql/question-ranking
题目描述
查找所有已经分配部门的员工的last_name和first_name以及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,
`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`));

图片说明

题解

/* 写法1:输出是按employees表输出的,所以使用内连接查询时,必须将employees表放在前面。*/
select last_name,first_name,dept_no 
from employees,dept_emp 
where dept_emp.emp_no = employees.emp_no;    --主键相等

/* 写法2:左联接*/
select e.last_name,e.first_name,d.dept_no 
from dept_emp d left join employees e 
on d.emp_no = e.emp_no 
可以看到e中的员工是一个集合,而分配了部门的员工是这个集合中的子集,
这样部门匹配到的肯定都是分配到该部门员工的信息,直接使用左连接就ok

/* 写法3:内联接*/
select a.last_name, a.first_name, b.dept_no
from employees a inner join dept_emp b
on a.emp_no = b.emp_no

写法2需要动脑子,建议学会写法3。写法3的on也可以用where,但是建议用on。
  对于JOIN参与的表的关联操作,如果需要不满足连接条件的行也在我们的查询范围内的话,我们就必需把连接条件放在ON后面,而不能放在WHERE后面,如果我们把连接条件放在了WHERE后面,那幺所有的LEFT,RIGHT,等这些操作将不起任何作用,对于这种情况,它的效果就完全等同于INNER连接。对于那些不影响选择行的条件,放在ON或者WHERE后面就可以。


题目5:

https://www.nowcoder.com/practice/dbfafafb2ee2482aa390645abd4463bf?tpId=82&tqId=29757&tPage=1&rp=&ru=/ta/sql&qru=/ta/sql/question-ranking
题目描述
查找所有员工的last_name和first_name以及对应部门编号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,
`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`));

图片说明

题解

/* 写法1:*/
SELECT ep.last_name, ep.first_name, dp.dept_no 
FROM employees ep 
LEFT JOIN dept_emp dp
ON ep.emp_no = dp.emp_no

注意:
INNER JOIN 两边表同时有对应的数据,即任何一边缺失数据就不显示。
LEFT JOIN 会读取左边数据表的全部数据,即便右边表无对应数据。
RIGHT JOIN 会读取右边数据表的全部数据,即便左边表无对应数据。
  依题意,需要大部分employee表的数据,即使dept_emp中没有,所以是左联接。注意,此时的on换成where就不行了。


题目6:

https://www.nowcoder.com/practice/23142e7a23e4480781a3b978b5e0f33a?tpId=82&tqId=29758&tPage=1&rp=&ru=/ta/sql&qru=/ta/sql/question-ranking
题目描述
查找所有员工入职时候的薪水情况,给出emp_no以及salary, 并按照emp_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`));

图片说明

题解:2个列,从2个表中选的,

/* 写法1:利用 INNER JOIN 连接两张表*/
SELECT 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

/* 写法2:直接用逗号并列查询两张表*/
SELECT 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

/* 写法3:获取salaries中from_data中最小的对应的salary和emp_no,
因为salaries可能涨薪,所以这张表一个员工可能有多条记录。*/
SELECT emp_no,salary 
FROM salaries 
GROUP BY emp_no 
HAVING min( from_date ) 
ORDER BY emp_no DESC

总结:以写法1和2为主,写法3不那么直观。