Leetcode | SQL | 刷题小记

注册完leetcode 进行SQL刷题

使用的SQL语言为mysql,环境为mysql 5.7。

  1. Second Highest Salary
    https://leetcode.com/problems/second-highest-salary/
Select 
    ifnull(
    (select 
        Distinct Salary
    from 
        Employee 
    order by 
        Salary desc
    limit 1 offset 1)
    ,null) as SecondHighestSalary

  • limit … offset …的用法
    针对已经排序的数据,可以选择任意排名的数据

  • ifnull()函数

  • 需要考虑NULL的问题

讨论区另外一个可行的解法:

select 
    max(salary) as SecondHighestSalary
from 
    Employee
where 
    salary <> (select
                max(salary) 
              from 
                Employee)

这可以有效避免null的问题。

从计算复杂度的角度来看,这两个答案应该没有区别。

  1. Rank Scores
    https://leetcode.com/problems/rank-scores/
select
  Score,
  (select 
    count(*) 
   from 
    (select 
        distinct score s 
     from 
        scores) tmp
     where 
       s>=score) Rank
from 
    Scores
order by 
    Score desc

答案来自于讨论区

dense_rank() 函数 也可以使用 但是 mysql server 5.7 不支持

  • 需要理解SQL的运算过程
  1. Consecutive Numbers
    https://leetcode.com/problems/consecutive-numbers/
# 针对每个Num 进行ID的分组排序 分组排序的代码类似于178
# 对于每一个数字找到其每一个排名 x :x+2 的ID
# 如果ID的差值为2 那么就是要找的数字

# 分组排序
select 
    distinct num as ConsecutiveNums
from
    (select 
        t1.num
        ,t1.ID - t2.ID as diff
    from 
        (select 
            id
            ,num
            ,(select 
                count(*) 
               from 
                (select 
                    id as tmp_id, 
                    num as tmp_num 
                 from
                    logs ) tmp 
               where 
                tmp_id >= id 
                and tmp_num = num) as rank
        from 
            logs) t1 
        -- 这是第一张表
    left join
        (select
            id
            ,num
            ,(select
                count(*)
            from
                (select
                    id as tmp_id
                    ,num as tmp_num 
                from
                    logs ) tmp 
            where
                tmp_id >= id and tmp_num = num) as rank
        from 
            logs) t2
-- 这是第二张表 两张表一样
on 
    t1.num = t2.num
    and t1.rank = t2.rank-2) tmp2 
where 
    diff = 2

看过答案之后就会觉得自己写的真是不好。

和答案相比,这个的答案可以n次连续的问题 只需要把代码中的最后出现的两个常数2 替换成 n

SELECT *
FROM
    Logs l1,
    Logs l2,
    Logs l3
WHERE
    l1.Id = l2.Id - 1
    AND l2.Id = l3.Id - 1
    AND l1.Num = l2.Num
    AND l2.Num = l3.Num
;

直接内连三个表格,这样出现在最后结果表格中就是我们所要找的答案。这里建议用 inner join 速度会比较快。实际好像没有什么区别

  1. Rising Temperature
    https://leetcode.com/problems/rising-temperature/
select 
    w1.id
from 
    weather w1 
inner join  
    weather w2
on 
    w1.RecordDate - w2.RecordDate = 1 and w1.Temperature > w2.Temperature

不是很明白这个答案为什么会出错。

select 
    w1.id
from 
    weather w1 
inner join  
    weather w2
on 
    datediff(w1.RecordDate, w2.RecordDate) = 1
    and w1.Temperature > w2.Temperature

运算不一样:
select '2019-01-31' - '2019-02-1'
计算的值为0 而不是1。

  1. Employees Earning More Than Their Managers
    https://leetcode.com/problems/employees-earning-more-than-their-managers/
select 
    e1.name as Employee
from 
    employee e1 
left join 
    employee e2 
on 
    e1.managerId = e2.id
    and e1.managerId is not null
where
    e1.salary > e2.salary

上面是正确答案。而下面的结果不正确:

select 
    e1.name as Employee
from 
    employee e1 
left join 
    employee e2 
on 
    e1.managerId = e2.id
    and e1.managerId is not null
    and e1.salary > e2.salary

为什么会出现这种情况?
参考资料
因为加 left join 会保留左边表格 在合并结果中 左表所有字段都保留。

  1. Department Highest Salary
    https://leetcode.com/problems/department-highest-salary/
select 
    d.name as Department
    ,a.name as Employee
    ,max_sa as Salary
from 
    Department d 
inner join 
    (select
        tmp.DepartmentID
        ,tmp.max_sa
        ,employee.Name
 
    from 
        (select
            DepartmentID
            ,max(salary) as max_sa
        from
            employee
        group by 1) tmp 
    left join
        employee 
    on 
        employee.DepartmentId = tmp.DepartmentID
        and employee.salary = tmp.max_sa) a
on a.DepartmentId = d.Id

  • 用了子查询相对比较复杂

  • 答案中的查询条件比较简单清晰

SELECT
    Department.name AS 'Department',
    Employee.name AS 'Employee',
    Salary
FROM
    Employee
        JOIN
    Department ON Employee.DepartmentId = Department.Id
WHERE
    (Employee.DepartmentId , Salary) IN
    (   SELECT
            DepartmentId, MAX(Salary)
        FROM
            Employee
        GROUP BY DepartmentId
	)
;

  1. Trips and Users
    https://leetcode.com/problems/trips-and-users/
select 
    trips.request_at as day
    ,round(sum(case 
                when 
                    position('cancelled' in trips.status) > 0
                then 
                    1
                else 0 
                end)
           /count(*)
           ,2) as "Cancellation Rate"
from
    trips
left join 
    users u1 
on
    u1.users_id = trips.driver_id
left join
    users u2
on 
    u2.users_id = trips.client_id
where
    u2.banned = "no" 
    and u1.banned = "no"
    and trips.Request_at between "2013-10-01" and "2013-10-03"
group by 
    trips.request_at
order by
    trips.request_at asc

计算速度比较慢

在表连接的时候,不太能理解 left join user u1 on u1.users_id = trips.client_id or u1.users_id = trips.client_id 出现的计算结果

会出现重复的结果,笛卡尔积会出现重复,主要是因为使用了不唯一的key。

复习一下left join的计算过程:以 A left join B on …为例

  1. 做 A和 B的笛卡尔积

  2. 对笛卡尔积表格中,所有进行条件筛选

  3. 筛选结果中 加入没有在筛选结果但是在A表中的记录 B表所用字段记为null

  4. Department Top Three Salaries
    https://leetcode.com/problems/department-top-three-salaries/

select
    Department.name as department
    ,tmp2.name as Employee
    ,tmp2.salary as salary
from 
    (select
        *
        ,(select
            count(*)
         from
            (select
                salary as s
                ,departmentId as dID
            from
                employee) tmp
        where
            tmp.s >= salary
            and departmentId = tmp.did) as rank
    from 
        employee) tmp2
inner join
    Department
on 
    tmp2.departmentId = Department.Id
        
where 
    rank < 3

这个只会出来三个人员。而不是薪水在前三的所用员工。

# Write your MySQL query statement below

# 按照Department 分组 排序
# 将排序号小于等于3 挑出来


# 分组排序怎么处理 
#

# 紧凑排名 前三

select
    Department.name as department
    ,tmp2.name as Employee
    ,tmp2.salary as salary
from 
    (select
        *
        ,(select
            count(*)
         from
            (select
                salary as s
                ,departmentId as dID
            from
                employee
            group by
                1,2
            having count(*) = 1) tmp
        where
            tmp.s > salary
            and departmentId = tmp.did) as rank
    from 
        employee) tmp2
inner join
    Department
on 
    tmp2.departmentId = Department.Id
        
where 
    rank < 3


觉得应该是对的,但是不知道问题在哪?

调试:在线环境SQLfiddle

  • 分组distinct出错,去掉having条件,运行正确。

修改后的SQL:

select
    Department.name as department
    ,tmp2.name as Employee
    ,tmp2.salary as salary
from 
    (select
        *
        ,(select
            count(*)
         from
            (select
                salary as s
                ,departmentId as dID
            from
                employee
            group by
                1,2) tmp
        where
            tmp.s > salary
            and departmentId = tmp.did) as rank
    from 
        employee) tmp2
inner join
    Department
on 
    tmp2.departmentId = Department.Id
        
where 
    rank < 3

答案的SQL更加简洁,在where语句中插入自查询。直接把rank变成一个自查询的结果。但是在运行时间上,答案运行时间更长。

SELECT
    d.Name AS 'Department', e1.Name AS 'Employee', e1.Salary
FROM
    Employee e1
        JOIN
    Department d ON e1.DepartmentId = d.Id
WHERE
    3 > (SELECT
            COUNT(DISTINCT e2.Salary)
        FROM
            Employee e2
        WHERE
            e2.Salary > e1.Salary
                AND e1.DepartmentId = e2.DepartmentId
        )
;
  1. Human Traffic of Stadium
    https://leetcode.com/problems/human-traffic-of-stadium/

# 排序 不能理解为什么没有结果

select
    stadium.id
    ,(select
        count(*)
     from
        (select
            id
         from
            stadium
         where
            people > 100
         ) tmp
     where
         tmp.id >=  id) as rank    
from
    stadium
where
    people > 100
    

答案的解法非常犀利。

select
    distinct s1.*
from
    stadium s1
    ,stadium s2
    ,stadium s3
where
    s1.people >= 100
    and s2.people >= 100
    and s3.people >= 100
    and ((s1.id - s2.id = 1  and s3.id - s1.id = 1)
        or (s2.id - s1.id = 1 and s3.id - s1.id = 2)
        or (s1.id - s2.id = 1 and s1.id - s3.id = 2))
order by s1.id
  1. Exchange Seats
    https://leetcode.com/problems/exchange-seats/

主要是利用数据库的左连接。

select
    s1.id
    ,(case when s2.student is null then s1.student else s2.student end) as student
from
    seat s1
left join
    seat s2
on
    (s1.id = s2.id - 1
    and s1.id mod 2 = 1)
or
    (s1.id = s2.id + 1
    and s1.id mod 2 = 0)
order by
    s1.id

在评论区看到的解法:

select
if(id < (select count(*) from seat), if(id mod 2=0, id-1, id+1), if(id mod 2=0, id-1, id)) as id, student
from seat
order by id asc;

四行代码解决问题,很高效。解题的思路和我的不一样,我的解题思路从互换name出发,而这个是直接交换id。

猜你喜欢

转载自blog.csdn.net/Minervar/article/details/87894662