Leetcode--SQL刷题(176-262)

二刷:
-- 176 求某个字段的第2个最大值,两种方法(先limit再group by也不错)
-- 177 熟悉函数的写法
-- 178 实现dense_rank()的经典方法,以及cast的使用
-- 180 同理:结合178体现了建造辅助列的核心思想
-- 184 学会使用多字段in,但是效果不好
-- 185 看自己二刷的方法,彻底掌握辅助列方法; 掌握官方那个方法中子查询的使用
-- 196 其实先做一个GROUP BY拿到ID,然后再做一个JOIN即可
-- 262 一轮的方法不对,看一下正确方案(应该有两个限制条件)

176.第二高的薪水
在这里插入图片描述

# Write your MySQL query statement below
答案一:常规思路
select IFNULL((select distinct(Salary) 
from Employee
order by Salary desc
limit 1,1),null) as SecondHighestSalary;
答案二:去掉最大值
select max(Salary) SecondHighestSalary
from employee
where
salary<(select max(salary) from employee)

177.第N高的薪水
在这里插入图片描述
FUNCTION如果查询不到结果会自动返回一个NULL.
所以我认为这个题没有必要嵌套IFNULL函数.
SET设置变量的时候是需要加分号的.

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
SET N=N-1;
RETURN (
      # Write your MySQL query statement below.
      SELECT DISTINCT salary FROM employee ORDER BY salary DESC LIMIT N,1);
END
但是如果这个demo要在实际系统中要运行,应该标明存储过程特性
delimiter &&
create function sal_grade(n int) returns int
reads sql data
begin
set N=n-1;
return(select distinct sal from emp order by sal desc limit N,1);
end
&&

178.分数排名
在这里插入图片描述

方法一:但是LeetCode中还不支持,并且很慢
# Write your MySQL query statement below
SELECT Score, dense_rank() over(ORDER BY score DESC) 'Rank' FROM Scores;

方法二:很慢
SELECT Score, (SELECT count(DISTINCT score) FROM Scores WHERE score >= s.score) AS Rank FROM Scores s ORDER BY Score DESC ;

方法三:超级好
mysql中变量不用事前申明,在用的时候直接用“@变量名”使用就可以了。
第一种用法:set @num=1; 或set @num:=1;  在set,update中都是赋值的意思
第二种用法:select @num:=1; 或 select @num:=字段名 from 表名 where ……
注意上面两种赋值符号,使用set时可以用“=”或“:=”,但是使用select时必须用“:=赋值”

SELECT Score,cast((
    CASE
    WHEN @prev = Score THEN @rk
    WHEN @prev := Score THEN @rk := @rk + 1
    else @rk := @rk + 1
    END) as UNSIGNED INTEGER) AS Rank
FROM Scores, (SELECT @rk := 0, @prev := null) r
ORDER BY Score DESC;

通过这里重点理解case函数.

180. 连续出现的数字
在这里插入图片描述
注意题目要求:连续出现三次

方法1: 0.9306
select distinct a.Num as ConsecutiveNums
from Logs as a left join logs b on a.num=b.num and a.id=b.id+1
left join logs c on b.num=c.num and b.id=c.id+1
where b.num is not null and c.num is not null;

方法2: 和方法1本质相同,注意差别.0.9569
select distinct a.Num as ConsecutiveNums
from Logs as a join logs b on a.num=b.num and a.id=b.id+1
join logs c on b.num=c.num and b.id=c.id+1;

方法3:超棒 0.9933  所以我们要尽量减少join操作
select distinct Num as ConsecutiveNums
from (
  select Num, 
    case 
      when @prev = Num then @count := @count + 1
      when @prev := Num then @count := 1
    end as CNT
  from Logs, (select @prev := null,@count := null) as t
) as temp
where temp.CNT = 3;

我发现优秀的代码都会   建造辅助列!!!

183. 从不订购的客户
在这里插入图片描述
在这里插入图片描述

select Name Customers from customers c left join orders o
on c.id=o.customerid
where customerid is null;

select Name Customers from customers
where id not in(select  customerid from orders where coustomerid  is not null);

这里我给出了两种答案
这两种方法差不多快 但是如果把第二种的限制where coustomerid is not null去掉会超级快

184.部门工资最高的员工
在这里插入图片描述
通过这个题理解:join连接有时候可以作为筛选方法使用!

方法一 官方解法  性能较差  但是要学会多个字段同时使用in
select d.Name Department, e.name Employee, e.Salary from department d join employee e
on d.id=e.departmentid
where (e.salary, e.departmentid) in
(select max(salary) m_salary, departmentid from employee group by departmentid);


方法二 三表连接
select d.Name Department, e.name Employee, e.Salary from department d join employee e
on d.id=e.departmentid
join (select max(salary) m_salary, departmentid from employee group by departmentid) s
on s.m_salary=e.salary and s.departmentid=e.departmentid;

185. 部门工资前三高的所有员工
在这里插入图片描述
在这里插入图片描述

自己二刷的代码,彻底掌握辅助列的方法
SELECT d.Name Department,t2.Name Employee, Salary
FROM
(select DepartmentId, Name, Salary, case when @dept=departmentid and @sal=Salary then @rk
									     when @dept=departmentid then @rk:=@rk+1
                                         else @rk:=1 end 'rank', @dept:=departmentid, @sal:= Salary
from employee, (select @dept:=0, @sal:=0, @rk:=0) t
order by departmentid, Salary desc) t2 join Department d
on t2.departmentid=d.Id
where t2.rank<=3 order by Department, Employee;

这个题目如果用串口函数去做就很简单 只是LeetCode中还不支持

窗口函数解法
select Name, Employee, Salary from
(select d.name Name, e.name Employee, e.Salary,  dense_rank() over(partition by e.departmentid order by e.salary desc) 排名 from
employee e join department d
on e.departmentid=d.id)
where 排名<4;

下面这个代码写的太漂亮了!!!

select d.Name Department,e.Name Employee, e.Salary from Employee e, Department d
where e.DepartmentId = d.Id
and (e.Salary , e.DepartmentId) in
(
select Salary,DepartmentId from(
SELECT 
    IF(@eTemp = a.DepartmentId
        @rowNum:=@rowNum + 1,
        @rowNum:=1) rowNum,
    @eTemp:=a.DepartmentId,
    Salary,
    DepartmentId
FROM
    (SELECT 
        Salary, DepartmentId
    FROM
        Employee e
    GROUP BY DepartmentId , Salary
    ORDER BY DepartmentId , Salary DESC) a,
    (SELECT @eTemp:=NULL, @rowNum:=0) b
) r where r.rowNum <=3
)
order by e.DepartmentId ,e.Salary desc;
我自己通过emp表写的一段代码 应该也不错
这可能是目前唯一一种可以实现根据两个字段排名的demo
通过这段代码理解 函数的执行顺序
select deptno, ename,sal,
if(@dep=deptno,null, @sa:=sal), 
(case
when @dep=deptno and @sa=sal then @ran:=@ran
when @dep=deptno and @sa:=sal then @ran:=@ran+1
when @dep:=deptno  then @ran:=1 end ) 排名
from emp, (select @dep:=0, @sa:=0, @ran:=0) t
order by deptno, sal desc;

最后看一下官方给的解释: 扩宽思路 但是性能不好
在这里插入图片描述
在这里插入图片描述
196. 删除重复的电子邮箱
在这里插入图片描述
这是一道简单级别的题目 但是自己想了好久都没做出来
题目要求对每个email只保留最小ID 那么其实我们很容易把最小的ID找出来… 然后not in

DELETE from Person 
Where Id not in (
    Select Id 
    From(
    Select MIN(Id) as id
    From Person 
    Group by Email
   ) t
)
其中表的别名t好像必须要加上,碰到很多这种情况了

197. 上升的温度
在这里插入图片描述
认识一下datediff函数
两个日期之间是可以直接相减的, 但是一旦数据不规整,代码就会报错.

方法一
select a.Id from  Weather as a join Weather as b 
on a.Temperature > b.Temperature and dateDiff(a.RecordDate,b.RecordDate) = 1 

方法二  超过99.9%
select
    Id
from
    (select w.*,
     @curd := w.RecordDate,
     @curt := w.Temperature,
     @isH := if(datediff(@curd,@pred) = 1 and @curt > @pret,1,0) as r,
     @pret := @curt,
     @pred := @curd
     from
        Weather w,
        (select 
            @curd := null,
            @pred := null,
            @curt := 0,
            @pret := 0,
            @isH := 0
        ) init
     order by w.RecordDate
    ) t

where
    t.r = 1;

理解一句很重要的话:
最后的order by 是影响其中的 r列的!! 他是根据其他列的计算结果得到的!
其实它有点像动态的!
而且这里的if表达式,是curd的本行和pred的上一行做比较!! 一定要理解这里!

自己写出了超过100%的代码!

select ID FROM
(select w.id,if(@tem<w.Temperature and datediff(w.RecordDate, @dat)=1,1,0) num,
@tem:=w.Temperature, @dat:=w.RecordDate
from Weather w, (select @tem:=0,@dat:=null ) t1
order by w.RecordDate) t2
where num=1;

262. 行程和用户
在这里插入图片描述
在这里插入图片描述
首先第一种方法是自己的做法 89% 自己用了一个子查询and内连接
第二种方法,是对第一种方法的改进 只使用了内连接 98%
通过第二种方法要学会: 在一个计算结果中使用多个函数.

方法一
select
    t.request_at Day, 
    (
        round(count(if(status != 'completed', status, null)) / count(status), 2)
    ) as 'Cancellation Rate'
from
    Users u inner join Trips t
on
    u.Users_id = t.Client_Id
and
    u.banned != 'Yes'
where
    t.Request_at >= '2013-10-01'
and
    t.Request_at <= '2013-10-03'
group by
    t.Request_at


方法二
select Day, round(cancell/number,2) 'Cancellation Rate' from
(select t.Request_at Day, count(*) number, sum(if(t.Status like "completed", 0, 1)) cancell
from trips t join users u
on t.Client_Id=u.Users_Id and u.Banned  like "NO"
where t.request_at>='2013-10-01' and t.request_at<='2013-10-03'
group by t.Request_at
 ) tab;
-- 上面的方法是不对的!应该是两个限制条件!(只是官网可以通过...)

SELECT T.request_at AS `Day`, 
	ROUND(SUM(IF(T.STATUS = 'completed',0,1))/COUNT(T.STATUS),2) AS `Cancellation Rate`
FROM Trips AS T
JOIN Users AS U1 ON (T.client_id = U1.users_id AND U1.banned ='No')
JOIN Users AS U2 ON (T.driver_id = U2.users_id AND U2.banned ='No')
WHERE T.request_at BETWEEN '2013-10-01' AND '2013-10-03'
GROUP BY T.request_at;
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值