牛客网数据库SQL实战解析(21-30题)

牛客网SQL刷题地址: https://www.nowcoder.com/ta/sql?page=0

牛客网数据库SQL实战解析(01-10题): https://blog.csdn.net/u010666669/article/details/104763370

牛客网数据库SQL实战解析(11-20题): https://blog.csdn.net/u010666669/article/details/104863298

牛客网数据库SQL实战解析(21-30题): https://blog.csdn.net/u010666669/article/details/104871373

牛客网数据库SQL实战解析(31-40题): https://blog.csdn.net/u010666669/article/details/104977904

牛客网数据库SQL实战解析(41-50题): https://blog.csdn.net/u010666669/article/details/104979427

牛客网数据库SQL实战解析(51-61题): https://blog.csdn.net/u010666669/article/details/104980372

第21题 查找所有员工自入职以来的薪水涨幅情况,给出员工编号emp_no以及其对应的薪水涨幅growth,并按照growth进行升序

SELECT e.emp_no, (s1.salary-s2.salary) AS growth
FROM employees e
INNER JOIN salaries s1
ON e.emp_no=s1.emp_no AND s1.to_date='9999-01-01'
INNER JOIN salaries s2
ON e.emp_no=s2.emp_no AND e.hire_date=s2.from_date
ORDER BY growth 
;

解析:查出所有员工当前的薪水和入职时的薪水,求出涨幅,再关联emp_no,做升序排列。

第22题 统计各个部门的工资记录数,给出部门编码dept_no、部门名称dept_name以及次数sum

SELECT a.dept_no,
       a.dept_name,
       count(1) as sum
FROM(
    select * from  departments dp
    LEFT JOIN dept_emp de
    ON dp.dept_no=de.dept_no
) a
LEFT JOIN salaries s
on a.emp_no=s.emp_no
GROUP BY dept_no, dept_name
;

解析:两次left join 关联即可得到所需要的数据。

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

SELECT emp_no,
       salary,
       DENSE_RANK() OVER(ORDER BY salary DESC) AS rank
FROM salaries
WHERE to_date='9999-01-01'
;

解析:根据给出的输出示例,排序是连续的,用dense_rank()即可,如果排名非连续,那么就用rank()函数。

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

SELECT de.dept_no, 
       s.emp_no, 
       s.salary 
FROM dept_emp AS de 
INNER JOIN salaries AS s 
ON s.emp_no = de.emp_no 
AND s.to_date = '9999-01-01'
WHERE de.emp_no NOT IN (
    SELECT emp_no 
    FROM dept_manager 
    WHERE to_date = '9999-01-01'
)
;

解析: 这道题可以不用上题目给的employees表,只要emp_no不在dept_manager表里面即可。

使用left join 时,on和where的区别:on是在生产临时表⑩的条件,而where是生成临时表后,对临时表加过滤条件。

25. 获取员工其当前的薪水比其manager当前薪水还高的相关信息,当前表示to_date='9999-01-01',
结果第一列给出员工的emp_no,
第二列给出其manager的manager_no,
第三列给出该员工当前的薪水emp_salary,
第四列给该员工对应的manager当前的薪水manager_salary

SELECT de.emp_no
     , dm.emp_no AS manager_no
     , s1.salary
     , s2.salary AS manager_salary
FROM dept_emp de
    ,salaries s1
    ,dept_manager dm
    ,salaries s2
WHERE de.emp_no=s1.emp_no
  AND de.dept_no=dm.dept_no
  AND dm.emp_no=s2.emp_no
  AND s1.salary>s2.salary
  AND de.to_date='9999-01-01'
  AND dm.to_date='9999-01-01'
  AND s1.to_date='9999-01-01'
  AND s2.to_date='9999-01-01'
;

题解二:

SELECT sem.emp_no
     , sdm.emp_no AS manager_no
     , sem.salary AS emp_salary
     , sdm.salary AS manager_salary
FROM(
    SELECT s.emp_no
         , s.salary
         , de.dept_no
    FROM salaries s
    INNER JOIN dept_emp de
    ON s.emp_no=de.emp_no
    AND s.to_date='9999-01-01'
    ) sem,
    (SELECT s.emp_no
          , s.salary
          , dm.dept_no
     FROM salaries s
     INNER JOIN dept_manager dm
     ON s.emp_no=dm.emp_no
     AND s.to_date='9999-01-01'
     ) sdm
WHERE sem.dept_no=sdm.dept_no
AND sem.salary>sdm.salary
;

题解:逻辑很简单,找到员工和对应的薪资,起个别名就可以了。显然第一种方法更简洁,不过第二种方法更易读。

26. 汇总各个部门当前员工的title类型的分配数目,结果给出部门编号dept_no、dept_name、其当前员工所有的title以及该类型title对应的数目count


SELECT de.dept_no, 
       dp.dept_name, 
       t.title, 
       COUNT(t.title) AS count
FROM titles t
INNER JOIN dept_emp de
ON de.emp_no=t.emp_no
AND de.to_date='9999-01-01'
AND t.to_date='9999-01-01'
INNER JOIN departments dp
ON de.dept_no=dp.dept_no
GROUP BY de.dept_no, dp.dept_name, t.title
;

解析:基本操作

27. 给出每个员工每年薪水涨幅超过5000的员工编号emp_no、薪水变更开始日期from_date以及薪水涨幅值salary_growth,并按照salary_growth逆序排列。

提示:在sqlite中获取datetime时间对应的年份函数为strftime('%Y', to_date)

SELECT s2.emp_no, s2.from_date, (s2.salary - s1.salary) AS salary_growth
FROM salaries AS s1, salaries AS s2
WHERE s1.emp_no = s2.emp_no 
AND salary_growth > 5000
AND (strftime("%Y",s2.to_date) - strftime("%Y",s1.to_date) = 1 
     OR strftime("%Y",s2.from_date) - strftime("%Y",s1.from_date) = 1 )
ORDER BY salary_growth DESC
;

解析:1、假设s1是涨薪水前的表,s2是涨薪水后的表,因为每个员工涨薪水的时间不全固定,有可能一年涨两次,有可能两年涨一次,所以每年薪水的涨幅,应该理解为两条薪水记录的from_date相同或to_date相同。

2、找到s1与s2符合要求的记录后,用s2的薪水减去s1的薪水,用salary_growth表示,加上限定条件 s1.emp_no = s2.emp_no AND salary_growth > 5000,即同一员工每年涨幅超过5000的记录

28. 查找描述信息中包括robot的电影对应的分类名称以及电影数目,而且还需要该分类对应电影数量>=5部

SELECT c.name, 
       COUNT(fc.film_id) 
FROM(
    SELECT category_id, 
           COUNT(film_id) AS category_num 
    FROM film_category  
    GROUP BY category_id 
    HAVING count(film_id)>=5
) AS cc,
film AS f, 
film_category AS fc, 
category AS c
WHERE  f.description LIKE '%robot%'
AND f.film_id = fc.film_id
AND c.category_id = fc.category_id
AND c.category_id=cc.category_id
;

解析:重要的是灵活复用已有的表。

29. 使用join查询方式找出没有分类的电影id以及名称

SELECT f.film_id, f.title
FROM film f
LEFT JOIN film_category fc
ON f.film_id=fc.film_id
WHERE category_id is null
;

30. 用子查询的方法找出属于Action分类的所有电影对应的title,description

SELECT f.title, f.description 
FROM (
    SELECT fc.film_id
    FROM film_category fc
    LEFT JOIN category c
    ON fc.category_id=c.category_id
    WHERE c.name='Action'
) t
LEFT JOIN film f
ON t.film_id=f.film_id
;

题解二:

SELECT f.title,f.description 
FROM film AS f
WHERE f.film_id IN (
    SELECT fc.film_id 
    FROM film_category AS fc
    WHERE fc.category_id IN (
        SELECT c.category_id 
        FROM category AS c
        WHERE c.name = 'Action'
    )
);

解析:这倒题很简单,首先想到的就是题解1,用left join 做。但是题目要求的是用子查询实现,子查询也很简单,如题解二所示。

原文地址:https://www.cnblogs.com/bigband/p/13532463.html