力扣SQL刷题记录(1)

力扣题目资源来自“熊大的数据分析之路”,写下这系列练习记录是为了自己实践一遍,非商业用途。

1. 组合两个表

表1:Person

+-------------+---------+

| 列名        | 类型    |

+-------------+---------+

| PersonId    | int    |

| FirstName  | varchar |

| LastName    | varchar |

+-------------+---------+

PersonId 是上表主键

表2:Address

+-------------+---------+

| 列名        | 类型    |

+-------------+---------+

| AddressId  | int    |

| PersonId    | int    |

| City        | varchar |

| State      | varchar |

+-------------+---------+

AddressId 是上表主键

编写一个 SQL 查询,满足条件:无论 person 是否有地址信息,都需要基于上述两表提供 person 的以下信息:

FirstName, LastName, City, State

select FirstName, LastName, City, State

from Person left join AddressId

where Person.PersonId=Address.PersonId

这道题好基础,以Person为主表,进行左链接即可

2. 第二高的薪水

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

+----+--------+

| Id | Salary |

+----+--------+

| 1  | 100    |

| 2  | 200    |

| 3  | 300    |

+----+--------+

例如上述Employee表,SQL查询应该返回200作为第二高的薪水。如果不存在第二高的薪水,那么查询应返回null。

+---------------------+

| SecondHighestSalary |

+---------------------+

| 200                |

+---------------------+

我的想法是:在排除掉最高薪资后的数据内,选取其最高薪资,即第二高薪资。所以有了这个代码:

select max(Salary) as SecondHighestSalary

from employee

where salary< (select max(salary) from employee)

另外学习到了一种排序函数的写法:

select min(salary) "SecondHighestSalary"

from(select salary,dense_rank()over(order by salary desc) as rn

from Employee)

where rn=2;

知识点:

rank() over(partition by A order by B) :按照A进行分组,分组里面的数据按照B进行排序,over即在什么之上,rank()即跳跃排序;

比如存在两个第一名,接下来就是第三名。

dense_rank():  连续排序,如果有两个第一名时,接下来第三个仍然是第二名。

附文章:https://www.jb51.net/article/20631.htm

3. 第N高的薪水

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

+----+--------+

| Id | Salary |

+----+--------+

| 1  | 100    |

| 2  | 200    |

| 3  | 300    |

+----+--------+

例如上述Employee表,n = 2 时,应返回第二高的薪水200。如果不存在第高的薪水,那么查询应返回null。

+------------------------+

| getNthHighestSalary(2) |

+------------------------+

| 200                    |

+------------------------+

解1:

CREATE FUNCTION getNthHighestSalary(@N INT) RETURNS INT AS

BEGIN

    RETURN (

        /* Write your T-SQL query statement below. */

    select

        distinct salary

    from

        (select salary,dense_rank() over (order by salary desc) as rank

        from employee) as t

    where rank=@N

    );

这个解法是先将薪资表进行连续排序(dense_rank函数),然后取题目要求的第N名,最后进行去重。

————————————————

解2:或者可以这样:

Write your MySQL query statement below:

select distinct Salary

from Employee  as e1

where N = (select  count(distinct e1.Salary)

from Employe e1,e2 

where e1.Salary <= e2.Salary )  

这个是不用连续排序(dense_rank函数)进行的薪资排序,把两张薪资表进行逐一的比较,把比较后的结果进行计数,比如薪资表有5个不同的数据:1000,500,2000,500,3000.表1内A薪资3000,和表2比较后,表1只有1个小于等于3000的薪资记录,那么count(distinct e1.Salary)则为1,3000这一薪资记录也排名第一;同理薪资500的话,和表2比较后,表1有5个小于等于500的薪资记录,去重后是4个;这个想了好久,枚举数据之后终于懂了 _(:з」∠)_ 

————————————————

解3:还有一个更简单的,学习到了

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT

BEGIN

  declare M INT;

  SET M=N-1;

  RETURN (

      # Write your MySQL query statement below.

      select ifnull((select distinct Salary from Employee order by Salary desc limit M,1),null) as getNthHighestSalary

  );

END

————————————————

知识点:*IFNULL() 函数用于判断第一个表达式是否为 NULL,如果为 NULL 则返回第二个参数的值,如果不为 NULL 则返回第一个参数的值。

*LIMIT M OFFSET L 

select * from table limit m,n

其中m是指记录开始的index,从源0开始,表示第一条记录

n是指从第m+1条开始,取n条。

select * from tablename limit 2,4

即取出第3条至第6条,4条记录

如果想去薪资第三名的数据,那么N=3,M=2,题中即从第M(2)行起,取1行数据,即取第三名薪资记录。

4. 分数排名

编写一个 SQL 查询来实现分数排名。如果两个分数相同,则两个分数排名(Rank)相同。请注意,平分后的下一个名次应该是下一个连续的整数值。换句话说,名次之间不应该有“间隔”。

+----+-------+

| Id | Score |

+----+-------+

| 1  | 3.50  |

| 2  | 3.65  |

| 3  | 4.00  |

| 4  | 3.85  |

| 5  | 4.00  |

| 6  | 3.65  |

+----+-------+

例如,根据上述给定的Scores表,你的查询应该返回(按分数从高到低排列):

+-------+------+

| Score | Rank |

+-------+------+

| 4.00  | 1    |

| 4.00  | 1    |

| 3.85  | 2    |

| 3.65  | 3    |

| 3.65  | 3    |

| 3.50  | 4    |

+-------+------+

解1:(还是也连续排序函数)

select score as "Score", dense_rank() over(order by score desc) as "Rank"

from Scores;

还有一种解法2:

(select count(distinct b.Score) from Scores b 

where b.Score >= a.Score)asRank

from Scores a 

order by a.Score DESC;

也是延续上一道题的两表比较后,计数大于等于的记录个数,作为排序

“ 提取出大于等于a.Score的所有分数集合H,将H去重后的元素个数就是a.Score的排名。select a.Score as Score”---大佬的解释好清楚,沿用了

5. 连续出现的数字

编写一个 SQL 查询,查找所有至少连续出现三次的数字。

+----+-----+

| Id | Num |

+----+-----+

| 1  |  1  |

| 2  |  1  |

| 3  |  1  |

| 4  |  2  |

| 5  |  1  |

| 6  |  2  |

| 7  |  2  |

+----+-----+

例如,给定上面的Logs表,1是唯一连续出现至少三次的数字。

+-----------------+

| ConsecutiveNums |

+-----------------+

| 1              |

+-----------------+

解:

select distinct a.Num as"ConsecutiveNums"

from Logsa, Logsb, Logsc

where a.Id=b.Id-1 and a.Id=c.Id-2 

and a.Num=b.Num and a.Num=c.Num;

解法就是把logs表*3,再把三张表的ID进行连续三次的where子句筛选,这题应该是可以对应连续登陆、连续购买···这类的情况的。

6. 超过经理收入的员工

Employee表包含所有员工,他们的经理也属于员工。每个员工都有一个 Id,此外还有一列对应员工的经理的 Id。

+----+-------+--------+-----------+

| Id | Name  | Salary | ManagerId |

+----+-------+--------+-----------+

| 1  | Joe  | 70000  | 3        |

| 2  | Henry | 80000  | 4        |

| 3  | Sam  | 60000  | NULL      |

| 4  | Max  | 90000  | NULL      |

+----+-------+--------+-----------+

给定Employee表,编写一个 SQL 查询,该查询可以获取收入超过他们经理的员工的姓名。在上面的表格中,Joe 是唯一一个收入超过他的经理的员工。

+----------+

| Employee |

+----------+

| Joe      |

+----------+

解:

select a.Name as "Employee"

from Employee a,Employee b

where a.ManagerId = b.Id  and a.Salary > b.Salary;

这道题经常遇到,解法是两表用ManagerId = Id相连接后,再去比较薪资即可。

7. 查找重复的电子邮箱

编写一个 SQL 查询,查找Person表中所有重复的电子邮箱。

示例:

+----+---------+

| Id | Email  |

+----+---------+

| 1  | [email protected] |

| 2  | [email protected] |

| 3  | [email protected] |

+----+---------+

根据以上输入,你的查询应返回以下结果:

+---------+

| Email  |

+---------+

| [email protected] |

+---------+

说明:所有电子邮箱都是小写字母。

解:select Email"Email"

from Person

group by Email

having count(Email) >1;

简单题,按邮箱分组后,筛选出计数项大于1的记录,就是重复的记录。

8. 从不订购的客户

某网站包含两个表,Customers表和Orders表。编写一个 SQL 查询,找出所有从不订购任何东西的客户。

Customers表:

+----+-------+

| Id | Name  |

+----+-------+

| 1  | Joe  |

| 2  | Henry |

| 3  | Sam  |

| 4  | Max  |

+----+-------+

Orders表:

+----+------------+

| Id | CustomerId |

+----+------------+

| 1  | 3          |

| 2  | 1          |

+----+------------+

例如给定上述表格,你的查询应返回:

+-----------+

| Customers |

+-----------+

| Henry    |

| Max      |

+-----------+

解:从不订购=订单表无记录,无ID,算是一种排除法吧。

select Name as "Customers"

from Customers

where Id not in (select CustomerId from Orders)

order by Name;

9. 部门工资最高的员工

Employee表包含所有员工信息,每个员工有其对应的 Id, salary 和 department Id。

+----+-------+--------+--------------+

| Id | Name  | Salary | DepartmentId |

+----+-------+--------+--------------+

| 1  | Joe  | 70000  | 1            |

| 2  | Henry | 80000  | 2            |

| 3  | Sam  | 60000  | 2            |

| 4  | Max  | 90000  | 1            |

+----+-------+--------+--------------+

Department表包含公司所有部门的信息。

+----+----------+

| Id | Name    |

+----+----------+

| 1  | IT      |

| 2  | Sales    |

+----+----------+

编写一个 SQL 查询,找出每个部门工资最高的员工。例如,根据上述给定的表格,Max 在 IT 部门有最高工资,Henry 在 Sales 部门有最高工资。

+------------+----------+--------+

| Department | Employee | Salary |

+------------+----------+--------+

| IT        | Max      | 90000  |

| Sales      | Henry    | 80000  |

+------------+----------+--------+

解:

select b.Name"Department",a.Name"Employee",Salary"Salary"

from Employee a,Department b

where DepartmentId = b.Id

and(Salary,DepartmentId) in

(select max(Salary),DepartmentId

from Employee

groupby DepartmentId);

先在员工信息表里按部门分组找出最高工资的员工,再连接部门表,显示要输出的信息。

10. 部门工资前三高的员工

Employee表包含所有员工信息,每个员工有其对应的 Id, salary 和 department Id 。

+----+-------+--------+--------------+

| Id | Name  | Salary | DepartmentId |

+----+-------+--------+--------------+

| 1  | Joe  | 70000  | 1            |

| 2  | Henry | 80000  | 2            |

| 3  | Sam  | 60000  | 2            |

| 4  | Max  | 90000  | 1            |

| 5  | Janet | 69000  | 1            |

| 6  | Randy | 85000  | 1            |

+----+-------+--------+--------------+

Department表包含公司所有部门的信息。

+----+----------+

| Id | Name    |

+----+----------+

| 1  | IT      |

| 2  | Sales    |

+----+----------+

编写一个 SQL 查询,找出每个部门工资前三高的员工。例如,根据上述给定的表格,查询结果应返回:

+------------+----------+--------+

| Department | Employee | Salary |

+------------+----------+--------+

| IT        | Max      | 90000  |

| IT        | Randy    | 85000  |

| IT        | Joe      | 70000  |

| Sales      | Henry    | 80000  |

| Sales      | Sam      | 60000  |

+------------+----------+--------+

解:

select d.name"Department",s.name"Employee",s.salary"Salary"

from(select departmentid,name,salary

from

(select departmentid,

name,

salary,

dense_rank()over(partition by departmentid order by salary desc) rn

from employee)

where rn <4) s,department d

where s.departmentid = d.id

思路:每个部门工资前三高的员工,首先用dense_rank()把部门前3高的记录作为表S,再用表s和部门名称表d进行连接,那么每个部门前三高薪资的记录就出来了。如果不支持dense_rank()函数的话,又要用两表比较,计数去重元素个数做排名的方法了。

↑是熊大的举例图

11. 删除重复的电子邮箱

编写一个 SQL 查询,来删除Person表中所有重复的电子邮箱,重复的邮箱里只保留Id 最小 的那个。

+----+------------------+

| Id | Email            |

+----+------------------+

| 1  | [email protected] |

| 2  | [email protected]  |

| 3  | [email protected] |

+----+------------------+

Id 是这个表的主键。

例如,在运行你的查询语句之后,上面的Person表应返回以下几行:

+----+------------------+

| Id | Email            |

+----+------------------+

| 1  | [email protected] |

| 2  | [email protected]  |

+----+------------------+

解:

delete d1

from Person d1,Person d2

where d1.Email = d2.Email and d1.Id > d2.Id;

知识点:删除语句,但是最好实际操作数据库的时候不要删除哦~

DELETE FROM table_name

WHERE some_column=some_value;

12. 上升的温度

给定一个Weather表,编写一个 SQL 查询,来查找与之前(昨天的)日期相比温度更高的所有日期的 Id。

+---------+------------------+------------------+

| Id(INT) | RecordDate(DATE) | Temperature(INT) |

+---------+------------------+------------------+

|      1 |      2015-01-01 |              10 |

|      2 |      2015-01-02 |              25 |

|      3 |      2015-01-03 |              20 |

|      4 |      2015-01-04 |              30 |

+---------+------------------+------------------+

例如,根据上述给定的Weather表格,返回如下 Id:

+----+

| Id |

+----+

|  2 |

|  4 |

+----+

解:

select w1.Id as "Id"

from Weather w1,Weather w2

where w1.RecordDate = w2.RecordDate +1 

and w1.Temperature > w2.Temperature;

老办法了,日期的连续要用+1来连接,然后在比较量表中气温即可。

不过要注意日期类型的字段,是可以这样计算的。

13. 行程和用户(重点复习,比较复杂)

Trips表中存所有出租车的行程信息。每段行程有唯一键 Id,Client_Id 和 Driver_Id 是Users表中 Users_Id 的外键。Status 是枚举类型,枚举成员为 (‘completed’, ‘cancelled_by_driver’, ‘cancelled_by_client’)。

+----+-----------+-----------+---------+--------------------+----------+

| Id | Client_Id | Driver_Id | City_Id |        Status      |Request_at|

+----+-----------+-----------+---------+--------------------+----------+

| 1  |    1    |    10    |    1    |    completed      |2013-10-01|

| 2  |    2    |    11    |    1    | cancelled_by_driver|2013-10-01|

| 3  |    3    |    12    |    6    |    completed      |2013-10-01|

| 4  |    4    |    13    |    6    | cancelled_by_client|2013-10-01|

| 5  |    1    |    10    |    1    |    completed      |2013-10-02|

| 6  |    2    |    11    |    6    |    completed      |2013-10-02|

| 7  |    3    |    12    |    6    |    completed      |2013-10-02|

| 8  |    2    |    12    |    12  |    completed      |2013-10-03|

| 9  |    3    |    10    |    12  |    completed      |2013-10-03|

| 10 |    4    |    13    |    12  | cancelled_by_driver|2013-10-03|

+----+-----------+-----------+---------+--------------------+----------+

Users表存所有用户。每个用户有唯一键 Users_Id。Banned 表示这个用户是否被禁止,Role 则是一个表示(‘client’, ‘driver’, ‘partner’)的枚举类型。

+----------+--------+--------+

| Users_Id | Banned |  Role  |

+----------+--------+--------+

|    1    |  No  | client |

|    2    |  Yes  | client |

|    3    |  No  | client |

|    4    |  No  | client |

|    10    |  No  | driver |

|    11    |  No  | driver |

|    12    |  No  | driver |

|    13    |  No  | driver |

+----------+--------+--------+

写一段 SQL 语句查出2013年10月1日 至2013年10月3日 期间非禁止用户的取消率。基于上表,你的 SQL 语句应返回如下结果,取消率(Cancellation Rate)保留两位小数。

+------------+-------------------+

|    Day    | Cancellation Rate |

+------------+-------------------+

| 2013-10-01 |      0.33        |

| 2013-10-02 |      0.00        |

| 2013-10-03 |      0.50        |

+------------+-------------------+

解:

整体思路是这样:

①限定日期2013年10月1日 至2013年10月3日,要按天进行分组并排序 ;

②非禁止用户,即Users表Banned字段为NO;

③取消率=取消次数/行程总数,还要进行小数点两位的保留;另外取消可能是用户,也可能是司机

select t2.Request_at as 'Day', ROUND(if_null(cancel,0)/count(t.id),2) as "Cancellation Rate"

from  ( select t.Request_at,count(t.Status) as cancel

from trips as t

inner join users c

on  c.Users_Id = t.Client_Id 

inner join users d

d.Users_Id = t.Driver_Id

where t.status !='completed' 

and t.Request_at between '2013-10-01' and '2013-10-03'

and c.Banned='No' and d.Banned='No') as t2

group by t2.Request_at

order by t2.Request_at

————————————————

好晕_(¦3」∠)_ 大致思路如上,

有一些坑:

1、比如2013/10/2这一天实际上没有取消订单的,不套用if_null的话,取消率算不出来

2、看了一些参考答案,最终我选择用内连接,感觉比较好懂

3、总结下来,要一步步拆解调试,嵌套再嵌套


刷了13道题,继续下面的13道题吧,这次对排序函数和LIMIT有了一些新的认识!

你可能感兴趣的:(力扣SQL刷题记录(1))