大家好,我是宁一。

SQL不仅仅是每个技术开发必须掌握的技能,

也是数据分析、商业分析招聘中,必考的能力。

这次给大家整理了SQL大厂春招的真题,每道题都加了独家详细解析。

整理完后,我的脑袋是蒙蒙的。

大厂就是大厂,这个烧脑程度,拜服!


之前也出过一篇大厂实战真题,大家可以一起食用。

2022年SQL大厂高频实战面试题(详细解析)

刷完这些题,相信大家能找到大厂出题的规律,大厂比较重视综合能力,题目不会只考一个知识点。

比如大多题目都综合考察了聚合函数、窗口函数、嵌套查询、索引、日期函数等知识点。

所以我们各个知识点都要熟练掌握。

这周六(3月26日)我会出SQL入门进阶课程。

不是广告,自己写的,免费的~

打算用最直白的语言给大家讲清楚SQL。从小白的角度入手,手把手教你学习SQL的入门以及进阶知识。

有些同学可能看过我之前的小程序实战课程,知道我的讲课风格就是:不说废话,只讲干货。

从最基础的增删改查,到进阶的视图、触发器、事件、事务等都会涉及。实战举例+作业巩固,争取用一门课让大家掌握SQL,可以顺利面试、参与工作。

感兴趣的同学可以关注蹲一下哦~

下面是大厂春招真题:

一、嵌套查询

嵌套查询,顾名思义,就是查询语句的嵌套,即在外部查询中还包含一个内部查询。

内部查询也叫子查询,查询结果可以作为一个临时表来使用,完成更为复杂表联结数据的检索功能。

嵌套查询是由内而外进行分析,子查询的结果作为主查询的查询条件。子查询中一般不使用order by子句,只能对最终查询结果进行排序。

经典问题:

下面表格是用户访问表users,记录了用户id(usr_id)和访问日期(log_date),查询出每个用户最近一次的登录记录和每个用户登录总次数(同一天多次登录认为是一次)。


实例结果:

解题思路:

根据示例结果,第二列current_day,是每个用户访问日期的最大值,直接用MAX聚合函数就可以。

第三列sum_day是每个用户登录总次数,因为同一天多次登录认为是一次,所以通过将原表log_date列格式化去掉时间,得出用户登录的日期,比如2021-05-03 01:00:08 格式化成 2021-05-03。我们就可以将日期去重,得到每个用户的登录总次数。

第一步:先将原表log_date列,通过DATE_FORMAT函数,进行日期的格式化。
SELECT
  usr_id,
  log_date,
  DATE_FORMAT(log_date,'%Y-%m-%d') AS days
FROM Users

第二步:再根据第一步得到的结果,将数据通过usr_id分组,并通过MAX聚合函数得到每组log_date列的最大值(也就是最近一次登录日期),通过COUNT聚合函数,得到用户登录总次数。
SELECT usr_id,
  MAX(log_date) AS current_day,
  COUNT(DISTINCT days) AS sum_day
FROM(
  SELECT
    usr_id,
    log_date,
    DATE_FORMAT(log_date,'%Y-%m-%d') AS days
  FROM Users
) AS a
GROUP BY usr_id;

二、大厂综合考察真题

这道题涵盖的知识点很多,包括索引、日期函数、聚合函数、窗口函数、嵌套查询。

能做出来,说明SQL掌握的已经很棒了,可以放心去面试。

经典问题:

我们现在有一个数据表DateTable,如下:

问题一:对数据表DateTable的dates列添加索引。

问题二:通过SQL语句,输入下方结果。

问题一:

这个就是考察索引语法的,很简单。

给DateTable表的dates列添加一个名为index_date的索引

CREATE INDEX index_date ON DateTable(dates);

查看索引

SHOW INDEX FROM DateTable;

问题二:

解题思路:

我们查看输出结果,第一列是年,第二列是月,这两列直接用日期函数就可以得到。

第四列,是将cost列按年汇总,第五列是将cost列全部汇总,因为每一行都有汇总记录,所以我们不应该单纯用聚合函数SUM来计算,而是用窗口函数+聚合函数SUM。

第一步:我们先将前三列查询展示出来。
SELECT dates,
  YEAR(dates) AS years,
  MONTH(dates) AS months,
  SUM(cost) AS cost
FROM DateTable
GROUP BY dates

第二步:再根据第一步得到的结果,通过聚合函数SUM,结合窗口函数得到四五列。对窗口函数不了解的盆友,可以点击上一篇文章了解~
SELECT years,months,cost,
SUM(cost) OVER(PARTITION BY years ORDER BY months) ysum,
SUM(cost) OVER(ORDER BY dates) sum
FROM(
  SELECT dates,
    YEAR(dates) AS years,
    MONTH(dates) AS months,
    SUM(cost) AS cost
  FROM DateTable
  GROUP BY dates
) AS a

可以使用下方语句快速建表测试:

create table DateTable (
dates datetime,
cost int
);
insert into DateTable values
("2020/07/12",10),
("2020/11/30",3),
("2019/2/9",20),
("2019/3/31",23),
("2022/2/8",99),
("2021/7/31",10);

三、字节春招真题

经典问题:

查询支付日期在2022.3.20之后的购买商品超过一次的用户,按照最新购买时间倒叙排列。

解题思路:

先找出2022.3.20之后的数据记录,再将这些记录根据user_id进行分组,通过COUNT聚合函数查出每组条数大于1的记录,最后还要按照最新购买时间倒叙排列。

SELECT user_id
FROM Products
WHERE purchase_date>"2022-03-20"
GROUP BY user_id
HAVING COUNT(*)>1销售
ORDER BY max(purchase_date) desc


这道题看着很简单,但是里面埋了几个陷阱需要注意:

  • having作用和where差不多,用来在分组之后,筛选分组才产生的数据。having后面可以直接跟聚合函数,如果要跟字段的话,这个字段必须在select后面提到过,where则不用

  • MySQL中,在使用聚合函数的时候,select后面的字段,必须出现在group by的后面,比如这道题中的user_id字段。

  • MySQL中,在使用聚合函数的时候,order_by后面的语句要注意使用聚合函数的字段,或者group by后面的字段。

上面的字段使用不太了解,没关系,我们来讲讲当一个查询语句同时出现where,group by,having,order by的时候,执行顺序和编写顺序,你就明白了。

FROM --> WHERE --> GROUP BY --> SELECT --> HAVING --> ORDER BY

我们再结合上面的题目看一下执行顺序:

1.执行where 语句对全表数据做筛选,FROM Products WHERE purchase_date>"2022-03-20”,返回第1个结果集。

2.针对第1个结果集使用group by分组,GROUP BY user_id,返回第2个结果集。

3.针对第2个结果集中的每1组数据执行SELECT user_id,有几组就执行几次,返回第3个结果集。

4.针对第3个结集执行HAVING COUNT(*)>1进行筛选,返回第4个结果集。

5.最后针对第4个结果集排序,ORDER BY max(purchase_date) desc

四、拼多多春招真题

经典问题:

现在有一个订单表orders,包括订单id(Oid)、商品id(product_id)、销售渠道(channel)、订单量(amount)。我们根据这个表,选取订单量前4的商品,并且选出每个商品订单量排名前2的销售渠道。

示例结果:

解题思路:

根据题目以及示例结果,我们最终要得到订单量最高的4个产品,以及每个产品对应排名前2的销售渠道。

我们可以先得到订单量排名前4的结果集1,再得到每个商品对应销售渠道的订单量排名结果集2,通过join将结果集1和结果集2连接得到最终结果。

第一步:首先获得订单量前4的商品,GROUP BY将产品分组之后,我们通过聚合函数SUM来获得每个产品的总订单量,命名为amt,再将amt倒序排序,通过limit 4得到订单量前4的商品。
SELECT product_id,
  SUM(amount) AS amt
FROM Orders
GROUP BY product_id
ORDER BY amt desc
LIMIT 4

第二步:我们再得到每个商品对应销售渠道的订单量排名
SELECT product_id,
  channel,
  ROW_NUMBER() OVER(PARTITION BY product_id ORDER BY amount DESC) AS rk
FROM Orders

第三步:将第一步和第二步的结果通过product_id列进行join链接,并限定条件第二步得到的rk排名列<=2。最终结果如下:
SELECT b.product_id,b.channel
FROM(
  SELECT product_id,
    SUM(amount) AS amt
  FROM Orders
  GROUP BY product_id
  ORDER BY amt desc
  LIMIT 4
) AS a
JOIN (
  SELECT product_id,
    channel,
    ROW_NUMBER() OVER(PARTITION BY product_id ORDER BY amount DESC) AS rk
  FROM Orders
) AS b
ON a.product_id = b.product_id
WHERE b.rk <=2

五、美团春招真题

现在有一个Products表,推荐出每个user_id,和他相似的用户所购买过的product。

要求:该用户已经购买过的不再推荐。

相似用户的定义:曾经购买过2种或2种以上的相同商品。

示例结果:

解题思路:

这道题我们先找出相似的用户,比如用户A和用户B是相似用户,我们需要将B购买过,而A没有购买过的产品推荐给A。这个跟实际业务很相似了,还需要费点脑筋的,我们来一步步拆解。

第一步:我们先找出相似的用户,也就是购买相同的商品>=2的用户。用到了自连接,找到不同用户对应相同商品的记录。再通过COUNT计算每个用户有多少条这样的记录。
SELECT a.user_id as uid_a,b.user_id as uid_b,
COUNT(DISTINCT b.product_id) as same_pro
FROM Products AS a
JOIN Products AS b
ON a.user_id != b.user_id 
AND a.product_id = b.product_id
GROUP BY uid_a,uid_b
HAVING same_pro>=2

第二步:将第一步得到的结果集,再通过uid_a连接Products表,连接条件是x.uid_a = y.user_id,这一步是为了得到product_id这一列。
SELECT x.uid_a,x.uid_b,y.product_id

FROM(
    SELECT a.user_id AS uid_a,b.user_id AS uid_b,
    COUNT(DISTINCT b.product_id) AS same_pro
    FROM Products AS a
    JOIN Products AS b
    ON a.user_id != b.user_id
    AND a.product_id = b.product_id
    GROUP BY uid_a,uid_b
    HAVING same_pro>=2
) AS x
JOIN Products AS y
ON x.uid_a = y.user_id

第三步:将第二步得到的结果集,再通过uid_b左连接Products表,连接条件是x.uid_b = z.user_id,y.product_id = z.product_id。

我们用的是左连接,即使不满足y.product_id = z.product_id,第二步获得的结果集也会全部显示,不满足条件的会显示null。这一步结果已经出来了,就是第四列为null 的记录。

SELECT x.uid_a,x.uid_b,y.product_id,z.product_id
FROM(
    SELECT a.user_id AS uid_a,b.user_id AS uid_b,
    COUNT(DISTINCT b.product_id) AS same_pro
    FROM Products AS a
    JOIN Products AS b
    ON a.user_id != b.user_id
    AND a.product_id = b.product_id
    GROUP BY uid_a,uid_b
    HAVING same_pro>=2
) AS x
JOIN Products AS y
ON x.uid_a = y.user_id
LEFT JOIN Products AS z
ON x.uid_b = z.user_id
AND y.product_id = z.product_id

第四步:根据第三步获得的结果集,我们判断第四列z.product_id为null,并且显示第二列x.uid_b和第三列y.product_id,就是最终的答案了。

最终结果如下:

SELECT x.uid_b AS user_id,y.product_id
FROM(
    SELECT a.user_id AS uid_a,b.user_id AS uid_b,
    COUNT(DISTINCT b.product_id) AS same_pro
    FROM Products AS a
    JOIN Products AS b
    ON a.user_id != b.user_id
    AND a.product_id = b.product_id
    GROUP BY uid_a,uid_b
    HAVING same_pro>=2
) AS x
JOIN Products AS y
ON x.uid_a = y.user_id
LEFT JOIN Products AS z
ON x.uid_b = z.user_id
AND y.product_id = z.product_id
WHERE z.product_id IS NULL;

六、快手春招真题

现在有主播表a:包括主播id(upid)、直播间id(rid)、开播时间(stime)

观众表b:包括观众id(uid)、进入的直播间id(rid)、进入时间(intime)

找出开播三分钟内无人进入的直播房间号。

解题思路:

我们先通过左连接,得到主播对应的3分钟进入直播间的观众,如果没有对应的观众,就是我们要找的记录。

第一步:将主播表a和观众表b进行左连接,条件是直播间id(rid)相同,并且主播开播时间(stime)大于(观众进入时间减去3分钟)。符合这个条件的主播,说明开播3分钟内有观众进入直播间。

比如主播是12:10分开播,观众在12:12分进入,这符合开播3分钟内进入的要求。

用数据表示就是:观众时间减去3分钟,是12:09分,12:10分>12:09分,说明用户在开播3分钟内进入直播间。

SELECT a.upid,a.rid,b.uid
FROM a
LEFT JOIN b
ON a.rid = b.rid
AND a.stime > date_add(b.intime,INTERVAL -3 minute)

第二步:第一步得到的结果集,第三列b.uid如果为null,说明这个直播间开播3分钟内没有观众进入。所以我们再添加一个where条件就可以了。
SELECT a.upid,a.rid
FROM a
LEFT JOIN b
ON a.rid = b.rid
AND a.stime > date_add(b.intime,INTERVAL -3 minute)
WHERE b.uid IS NULL;

更多推荐

SQL大厂春招真题,独家详细解析