Bootstrap

【力扣 | SQL题 | 每日4题】力扣614,1355,1341,1555,2041

最近只写mid题了。

1. 力扣 614:二级关注者

1.1 题目:

表:Follow

+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| followee    | varchar |
| follower    | varchar |
+-------------+---------+
(followee, follower) 是该表的主键(具有唯一值的列的组合)。
该表的每一行表示关注者关注了社交网络上的被关注者。
不会有用户关注他们自己。

二级关注者 是指满足以下条件的用户:

  • 关注至少一个用户,
  • 被至少一个用户关注。

编写一个解决方案来报告 二级用户 及其关注者的数量。

返回按 follower 字典序排序 的结果表。

结果格式如下所示。

示例 1:

输入:
Follow table:
+----------+----------+
| followee | follower |
+----------+----------+
| Alice    | Bob      |
| Bob      | Cena     |
| Bob      | Donald   |
| Donald   | Edward   |
+----------+----------+
输出:
+----------+-----+
| follower | num |
+----------+-----+
| Bob      | 2   |
| Donald   | 1   |
+----------+-----+
解释:
用户 Bob 有 2 个关注者。Bob 是二级关注者,因为他关注了 Alice,所以我们把他包括在结果表中。
用户 Donald 有 1 个关注者。Donald 是二级关注者,因为他关注了 Bob,所以我们把他包括在结果表中。
用户 Alice 有 1 个关注者。Alice 不是二级关注者,但是她不关注任何人,所以我们不把她包括在结果表中。

1.2 思路:

看注释。

1.3 题解:

-- 因为二级关注者被至少一个用户关注。
-- 所以其followee肯定在follower 出现过(即有其他人关注了该二级关注者)
with tep as (
    select *
    from Follow
    where followee in (
        select follower
        from Follow
    )
)
-- 然后分组计数
select followee follower, count(*) num
from tep
group by followee
order by follower

2. 力扣1355:活动参与者

2.1 题目:

表: Friends

+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| id            | int     |
| name          | varchar |
| activity      | varchar |
+---------------+---------+
id 是朋友的 id,并且在 SQL 中,是该表的主键
name 是朋友的名字
activity 是朋友参加的活动的名字

表: Activities

+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| id            | int     |
| name          | varchar |
+---------------+---------+
在 SQL 中,id 是该表的主键
name 是活动的名字

找出那些既没有最多,也没有最少参与者的活动的名字。

Activities 表中的任意活动都有在 Friends 中参与过。

可以以 任何顺序 返回结果。

下面是返回结果格式的例子。

示例 1:

输入:
Friends 表:
+------+--------------+---------------+
| id   | name         | activity      |
+------+--------------+---------------+
| 1    | Jonathan D.  | Eating        |
| 2    | Jade W.      | Singing       |
| 3    | Victor J.    | Singing       |
| 4    | Elvis Q.     | Eating        |
| 5    | Daniel A.    | Eating        |
| 6    | Bob B.       | Horse Riding  |
+------+--------------+---------------+
Activities 表:
+------+--------------+
| id   | name         |
+------+--------------+
| 1    | Eating       |
| 2    | Singing      |
| 3    | Horse Riding |
+------+--------------+
输出:
+--------------+
| activity     |
+--------------+
| Singing      |
+--------------+
解释:
Eating 活动有三个人参加, 是最多人参加的活动 (Jonathan D. , Elvis Q. and Daniel A.)
Horse Riding 活动有一个人参加, 是最少人参加的活动 (Bob B.)
Singing 活动有两个人参加 (Victor J. and Jade W.)

2.2 思路:

看注释。先得到排名,然后去掉排名第一和倒一。

2.3 题解:

with tep1 as (
    -- 第一步:先分组计算每个活动的次数
    select activity, count(*) cnt
    from Friends 
    group by activity
), tep2 as (
    -- 第二步:计算每个活动依据次数得到的排名
    select activity, cnt, dense_rank() over (order by cnt) ranks
    from tep1
), tep3 as (
    -- 第三步:得到排名榜的第一名和最后一名的排名
    select first_value(ranks) over (order by ranks rows between unbounded preceding and unbounded following) value
    from tep2
    union all
    select last_value(ranks) over (order by ranks rows between unbounded preceding and unbounded following) value
    from tep2
)
-- 然后在where中过滤掉排名最高和最低的即可。
select activity
from tep2
where ranks not in (select * from tep3)

3. 力扣1341:电影评分

3.1 题目:

表:Movies

+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| movie_id      | int     |
| title         | varchar |
+---------------+---------+
movie_id 是这个表的主键(具有唯一值的列)。
title 是电影的名字。

表:Users

+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| user_id       | int     |
| name          | varchar |
+---------------+---------+
user_id 是表的主键(具有唯一值的列)。
'name' 列具有唯一值。

表:MovieRating

+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| movie_id      | int     |
| user_id       | int     |
| rating        | int     |
| created_at    | date    |
+---------------+---------+
(movie_id, user_id) 是这个表的主键(具有唯一值的列的组合)。
这个表包含用户在其评论中对电影的评分 rating 。
created_at 是用户的点评日期。 

请你编写一个解决方案:

  • 查找评论电影数量最多的用户名。如果出现平局,返回字典序较小的用户名。
  • 查找在 February 2020 平均评分最高 的电影名称。如果出现平局,返回字典序较小的电影名称。

字典序 ,即按字母在字典中出现顺序对字符串排序,字典序较小则意味着排序靠前。

返回结果格式如下例所示。

示例 1:

输入:
Movies 表:
+-------------+--------------+
| movie_id    |  title       |
+-------------+--------------+
| 1           | Avengers     |
| 2           | Frozen 2     |
| 3           | Joker        |
+-------------+--------------+
Users 表:
+-------------+--------------+
| user_id     |  name        |
+-------------+--------------+
| 1           | Daniel       |
| 2           | Monica       |
| 3           | Maria        |
| 4           | James        |
+-------------+--------------+
MovieRating 表:
+-------------+--------------+--------------+-------------+
| movie_id    | user_id      | rating       | created_at  |
+-------------+--------------+--------------+-------------+
| 1           | 1            | 3            | 2020-01-12  |
| 1           | 2            | 4            | 2020-02-11  |
| 1           | 3            | 2            | 2020-02-12  |
| 1           | 4            | 1            | 2020-01-01  |
| 2           | 1            | 5            | 2020-02-17  | 
| 2           | 2            | 2            | 2020-02-01  | 
| 2           | 3            | 2            | 2020-03-01  |
| 3           | 1            | 3            | 2020-02-22  | 
| 3           | 2            | 4            | 2020-02-25  | 
+-------------+--------------+--------------+-------------+
输出:
Result 表:
+--------------+
| results      |
+--------------+
| Daniel       |
| Frozen 2     |
+--------------+
解释:
Daniel 和 Monica 都点评了 3 部电影("Avengers", "Frozen 2" 和 "Joker") 但是 Daniel 字典序比较小。
Frozen 2 和 Joker 在 2 月的评分都是 3.5,但是 Frozen 2 的字典序比较小。

3.2 思路:

相当于将两个窗口函数排名的问题糅合了起来,然后最终输出union all。

3.3 题解:

with tep1 as (
    -- 第一步: 得到每个用户名评论电影的个数
    select u.user_id user_id, name, count(*) cnt
    from Users u 
    join MovieRating m 
    on u.user_id = m.user_id
    group by u.user_id
), tep2 as (
    -- 第二步:得到每个用户名个数的排名
    -- 最后where限制ranks即可得到第一名
    select name, row_number() over (order by cnt desc, name) ranks
    from tep1
), tep3 as (
    -- 第一步: 得到每个电影的均分
    select title, avg(rating) avg_rat
    from MovieRating m1
    join Movies m2 
    on m1.movie_id = m2.movie_id
    and created_at >= '2020-02-01' and created_at < '2020-03-01'
    group by title
), tep4 as (
    -- 第二步:将每个电影的均分进行排名
    -- 最后where限制ranks即可
    select title, row_number() over (order by avg_rat desc, title) ranks
    from tep3
)
-- 将两个输出union all
select name results
from tep2
where ranks = 1
union all
select title results
from tep4
where ranks = 1

4. 力扣1555:银行账户概要

4.1 题目:

用户表: Users

+--------------+---------+
| Column Name  | Type    |
+--------------+---------+
| user_id      | int     |
| user_name    | varchar |
| credit       | int     |
+--------------+---------+
user_id 是这个表的主键(具有唯一值的列)。
表中的每一列包含每一个用户当前的额度信息。

交易表:Transactions

+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| trans_id      | int     |
| paid_by       | int     |
| paid_to       | int     |
| amount        | int     |
| transacted_on | date    |
+---------------+---------+
trans_id 是这个表的主键(具有唯一值的列)。
表中的每一列包含银行的交易信息。
ID 为 paid_by 的用户给 ID 为 paid_to 的用户转账。

力扣银行 (LCB) 帮助程序员们完成虚拟支付。我们的银行在表 Transaction 中记录每条交易信息,我们要查询每个用户的当前余额,并检查他们是否已透支(当前额度小于 0)。

编写解决方案报告:

  • user_id 用户 ID
  • user_name 用户名
  • credit 完成交易后的余额
  • credit_limit_breached 检查是否透支 ("Yes" 或 "No")

任意顺序返回结果表。

结果格式见如下所示。

示例 1:

输入:
Users 表:
+------------+--------------+-------------+
| user_id    | user_name    | credit      |
+------------+--------------+-------------+
| 1          | Moustafa     | 100         |
| 2          | Jonathan     | 200         |
| 3          | Winston      | 10000       |
| 4          | Luis         | 800         | 
+------------+--------------+-------------+

Transactions 表:
+------------+------------+------------+----------+---------------+
| trans_id   | paid_by    | paid_to    | amount   | transacted_on |
+------------+------------+------------+----------+---------------+
| 1          | 1          | 3          | 400      | 2020-08-01    |
| 2          | 3          | 2          | 500      | 2020-08-02    |
| 3          | 2          | 1          | 200      | 2020-08-03    |
+------------+------------+------------+----------+---------------+

输出:
+------------+------------+------------+-----------------------+
| user_id    | user_name  | credit     | credit_limit_breached |
+------------+------------+------------+-----------------------+
| 1          | Moustafa   | -100       | Yes                   | 
| 2          | Jonathan   | 500        | No                    |
| 3          | Winston    | 9900       | No                    |
| 4          | Luis       | 800        | No                    |
+------------+------------+------------+-----------------------+
Moustafa 在 "2020-08-01" 支付了 $400 并在 "2020-08-03" 收到了 $200 ,当前额度 (100 -400 +200) = -$100
Jonathan 在 "2020-08-02" 收到了 $500 并在 "2020-08-08" 支付了 $200 ,当前额度 (200 +500 -200) = $500
Winston 在 "2020-08-01" 收到了 $400 并在 "2020-08-03" 支付了 $500 ,当前额度 (10000 +400 -500) = $9900
Luis 未收到任何转账信息,额度 = $800

4.2 思路:

看注释。

4.3 题解:

-- ifNull函数是为了避免没有交易记录出现null的情况
with tep as (
    select user_id, user_name, 
    credit - ifNull((select sum(amount) from Transactions where paid_by = user_id), 0)
    + ifNull((select sum(amount) from Transactions where paid_to = user_id), 0) credit
    from Users
)
-- 增加字段判断是否透支
-- 为啥不将credit_limit_breached放在tep临时表中作为最后的一个字段
-- 因为如果其放在credit字段后
-- 其判断的是Users表中的credit,而不是其计算得到的字段credit
-- 因为计算得到的字段是将上面几行代码当成一张表的credit字段
select user_id, user_name, credit, 
case when credit >= 0 then 'No'
else 'Yes'
end credit_limit_breached
from tep

5. 力扣2041:面试中被录取的候选人

5.1 题目:

表:Candidates

+--------------+----------+
| Column Name  | Type     |
+--------------+----------+
| candidate_id | int      |
| name         | varchar  |
| years_of_exp | int      |
| interview_id | int      |
+--------------+----------+
candidate_id 是这个表的主键(具有唯一值的列)。
该表的每一行都表示候选人的姓名、工作年限以及面试 ID 。

表:Rounds

+--------------+------+
| Column Name  | Type |
+--------------+------+
| interview_id | int  |
| round_id     | int  |
| score        | int  |
+--------------+------+
(interview_id, round_id)是本表的主键(具有唯一值的列的组合)。
本表的每一行都表示一轮面试的分数

编写解决方案,找出 至少有两年 工作经验、且面试分数之和 严格大于 15 的候选人的 ID 。

可以以 任何顺序 返回结果表。

查询结果的格式如下例所示。

示例 1:

输入:
Candidates table:
+--------------+---------+--------------+--------------+
| candidate_id | name    | years_of_exp | interview_id |
+--------------+---------+--------------+--------------+
| 11           | Atticus | 1            | 101          |
| 9            | Ruben   | 6            | 104          |
| 6            | Aliza   | 10           | 109          |
| 8            | Alfredo | 0            | 107          |
+--------------+---------+--------------+--------------+
Rounds table:
+--------------+----------+-------+
| interview_id | round_id | score |
+--------------+----------+-------+
| 109          | 3        | 4     |
| 101          | 2        | 8     |
| 109          | 4        | 1     |
| 107          | 1        | 3     |
| 104          | 3        | 6     |
| 109          | 1        | 4     |
| 104          | 4        | 7     |
| 104          | 1        | 2     |
| 109          | 2        | 1     |
| 104          | 2        | 7     |
| 107          | 2        | 3     |
| 101          | 1        | 8     |
+--------------+----------+-------+
输出:
+--------------+
| candidate_id |
+--------------+
| 9            |
+--------------+
解释:
- 候选人 11 :总分是 16 ,1 年工作经验。由于工作年限,不列入结果表。
- 候选人 9 :总分是 22 ,6 年工作经验。列入结果表。
- 候选人 6 :总分是 10 ,10 年工作经验。由于分数不足,不列入结果表。
- 候选人 8 :总分是 6 ,0 年工作经验。由于工作年限和分数,不列入结果表。

5.2 思路:

两个要求两个where过滤即可。

5.3 题解:

-- 第一步:过滤工作经验少于两年的人
with tep as (
    select candidate_id, interview_id
    from Candidates
    where years_of_exp >= 2
), temp as (
    -- 第二步:计算每个候选人的面试分数
    select candidate_id, sum(score) sum_score
    from tep t
    join Rounds r
    on t.interview_id = r.interview_id
    group by candidate_id
)
-- 第三步:过滤面试分数和不足15的候选人
select candidate_id
from temp
where sum_score > 15

;