高频 SQL 50 题(基础版)- 04. 排序和分组

256 阅读5分钟

来源:高频 SQL 50 题(基础版)leetcode.cn/studyplan/s…

2356. 每位教师所教授的科目种类的数量

查询每位老师在大学里教授的科目种类的数量。

以 任意顺序 返回结果表。

查询结果格式示例如下。

思路 + SQL 1

这里的难点是通过 teacher_id 分组后,科目很有可能重复,因为了老师会在不同的系里教同一门课。怎么解决呢?加个 distinct 去除组中的重复 subject_id 就完事了。

select
  teacher_id,
  count(distinct subject_id) as cnt
from
  Teacher
group by
  teacher_id;

思路 + SQL 2

用另外一种方法,就是在已经去重后的表中进行直接计数,就可以获得结果了。

select
  teacher_id,
  count(*) as cnt
from
  (
    select
      distinct teacher_id,
      subject_id
    from
      teacher
  ) as t
group by
  teacher_id;

1141. 查询近30天活跃用户数

编写解决方案,统计截至 2019-07-27(包含2019-07-27),近 30 天的每日活跃用户数(当天只要有一条活动记录,即为活跃用户)。

以 任意顺序 返回结果表。

结果示例如下。

思路 + SQL

这题较为简单,只不过考察看题和日期函数的用法。这里统计近 30 天的每日活跃用户,那么就需要将比 2019-07-27 小,但是不能小超过 30 天的所有活动天数找出来。然后得到的表用日期聚类一下,再用 distinct 统计用户数量即可。这里不能用 session_id 来统计用户,因为用户可能有多个 session_id,不用同一个机器访问都会有这问题。

这里需要额外提一下的是,dateDiff 的用法是,DATEDIFF(enddate, startdate),结果是 enddate - startdate 的值,以天数的形式。

select
  activity_date as day,
  count(distinct user_id) as active_users
from
  Activity
where
  dateDiff('2019-07-27', activity_date) >= 0
  and dateDiff('2019-07-27', activity_date) < 30
group by
  activity_date;

1084. 销售分析III

编写解决方案,报告2019年春季才售出的产品。即仅在 2019-01-01 至 2019-03-31(含)之间出售的商品。

以 任意顺序 返回结果表。

结果格式如下所示。

思路 + SQL

这题会理所应当地去连接表之后,然后通过日期范围查询:

select
  *
from
  Product p
  inner join Sales s on p.product_id = s.product_id
where
  s.sale_date between '2019-01-01'
  and '2019-03-31';

但是这样计算得到的结果是有问题的。因为想要搜索的是仅仅在春季才销售的产品:

有一个比较简单的方法是,通过计数来判断。如果在春季的销售数量等于统计的所有的销售数量,那么就说明仅仅在春季销售了。

  1. 两表连接,得到商品的销售和详细信息表:
select
  *
from
  Product p
  inner join Sales s on p.product_id = s.product_id;

  1. 分组后计数。根据 product_id 进行分组,对每组进行春季的销售统计和所有的销售统计。如果值相等,说明是仅在春季销售的。
select
  p.product_id,
  p.product_name
from
  Product p
  inner join Sales s on p.product_id = s.product_id
group by
  p.product_id
having
	-- 分组筛选要求:春季销售量 = 所有的销售量
  count(
    s.sale_date between '2019-01-01'
    and '2019-03-31'
    or null
  ) = count(s.sale_date);

这里使用了 or null 的写法,是如果不符合条件就记录为 null,而 null 不会被 count 计数。亲测 or null 比 if 函数还快。

596. 超过5名学生的课

查询 至少有5个学生 的所有班级。

以 任意顺序 返回结果表。

查询结果格式如下所示。

SQL

送分题不解释。

select
  class
from
  Courses
group by
  class
having
  count(student) >= 5;

1729. 求关注者的数量

编写解决方案,对于每一个用户,返回该用户的关注者数量。

按 user_id 的顺序返回结果表。

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

SQL

仍然是一题送分题。

select
  user_id,
  count(follower_id) as followers_count
from
  Followers
group by
  user_id
order by
  user_id asc;

619. 只出现一次的最大数字

单一数字 是在 MyNumbers 表中只出现一次的数字。

找出最大的 单一数字 。如果不存在 单一数字 ,则返回 null 。

查询结果如下例所示。

思路 + SQL 1

这题很常规地通过分组和 count 方法来计算,理论上可以得到只出现一次的最大值。

select
  num
from
  MyNumbers
group by
  num
having
  count(num) = 1
order by
  num desc
limit
  1;

但是在没有任何符合条件的情况下,返回的是个空表,而不是 null:

空表格代表没有任何的输出,那再根据这个结果再怎么加工都没用。

所以在外层再添加一个 聚类函数 来获取,就可以得到 null 了。下面是一个总结表,可以在空表的查询中返回 null:

select
  max(num) as num
from
  (
    select
      num
    from
      MyNumbers
    group by
      num
    having
      count(num) = 1
    order by
      num desc
    limit
      1
  ) t;

思路 + SQL 2

用 select 来接受,如果 select 没有获取到任何值,会返回 null。

select
  (
    select
      num
    from
      MyNumbers
    group by
      num
    having
      count(num) = 1
    order by
      num desc
    limit
      1
  ) as num;

总结

  • select 聚类函数等 as param1 from (空表) —> param1:null
  • select (空表) as param1 -> param1:null

其他

  • limit 语句无法出现新的 null 值
  • where 和 having 同样无法出现新的 null 值

1045. 买下所有产品的客户

编写解决方案,报告 Customer 表中购买了 Product 表中所有产品的客户的 id。

返回结果表 无顺序要求 。

返回结果格式如下所示。

思路 + SQL

有了上面的题目的经验,这次也使用 having 条件来过滤分组后的结果。筛选条件为,用户买的种类和 Product 表中的种类数量相等。

select
  customer_id
from
  Customer
group by
  customer_id
having
  count(distinct product_key) = (
    select
      count(product_key)
    from
      Product
  );