这段时间,刷了LeetCode的数据库题目,挺有感触的。在刷这些题之前,我只写过一般的增删改查,尤其是查询操作,使用的也不是很灵活,只知道最基本的用法。
去刷了题目之后真的学到了很多不论是新的知识点还是一些组合用法,总之对数据库操作尤其是dql的使用更加熟练了。感谢其他leetcoder对各种知识点和解题套路的分享,学会这些小操作后,写sql头不疼了,腰不酸了,睡得着了,吃饭也更香了。
根据这些题,我总结了一些常用的知识点、小技巧以及部分有代表性的题解。
这篇文章就来分享一些有价值的题解。题目有些多,这篇先展示其中八题的题解。
剩下的在这里
题目链接
这道题首先应该看出,结果是学生表和科目表的可卡尔积。
所以我们应该首先将学生和科目两个表直接联结形成笛卡尔积。
然后我们将这个表当做新的表,主键为学生id和科目组成的联合主键,再去联结考试表。
最后,根据联合主键进行分组和排序,统计出每个同学每个科目的参与次数。
SELECT
s.student_id,
s.student_name,
sb.subject_name,
COUNT(e.student_id) AS attended_exams
FROM
Students s
join Subjects sb
LEFT JOIN Examinations e
on e.subject_name = sb.subject_name and e.student_id = s.student_id
GROUP BY
s.student_id,sb.subject_name
ORDER BY
s.student_id,sb.subject_name;
题目链接
首先,国家表和天气表是关联关系,我们需要先将他们两个联结起来。
接着,需要筛选出所有11月份的天气信息
然后,对表按照国家进行分组
最后,根据每个国家的平均气温来确定该国家的气候类型。这里涉及到分支判断,推荐使用case-when来解决问题
SELECT
c.country_name,
CASE
WHEN AVG(w.weather_state) <= 15 THEN 'Cold'
WHEN AVG(w.weather_state) >= 25 THEN 'Hot'
ELSE 'Warm'
END AS weather_type
FROM
Countries c
JOIN Weather w ON c.country_id = w.country_id
WHERE
w.day LIKE '2019-11%'
GROUP BY c.country_name;
;
那我们就先使用子查询查询出所有团队的规模,记作表t
接着根据团队id联结两张表t和employee,输出其中员工id和团队规模两列即可。
select
employee_id,
team_size
from
employee e
join
(
select
team_id,
count(*) as team_size
from employee
group by team_id
) t on e.team_id = t.team_id
;
题目链接
这个题目,需要我们统计每个广告的点击操作数占点击和观看操作的比率
首先,我们需要根据广告id进行分组。
接着,我们在统计的时候可以使用聚合函数配合case-when来完成筛选
最后不要忘记对答案进行保留两位小数和null处理
select
ad_id,
ifnull(round(sum(case when action = 'Clicked' then 1 else 0 end) / sum(case when action = 'Clicked' or action = 'viewed' then 1 else 0 end) * 100,2),0) as ctr
from
ads
group by
ad_id
order by
ctr desc,ad_id
;
题目链接
这道题需要我们制作一个表格,表格的每一行分别是统计一个数据段的内容。
由于它需要展示所有四个分段,即使该分段没有数据,即total为0,我们就不便使用常规的查询方法来查询结果。
对于这样的需求,我们可以将四个结果分开查询,并使用union将他们连接起来。这也是这道题的关键所在。
select '[0-5>' as bin,count(*) as total
from sessions
where duration / 60 >= 0 and duration / 60 < 5
union
select '[5-10>' as bin, count(*) as total
from sessions
where duration / 60 >= 5 and duration / 60 < 10
union
select '[10-15>' as bin,count(*) as total
from sessions
where duration / 60 >= 10 and duration / 60 < 15
union
select '15 or more' as bin,count(*) as total
from sessions
where duration / 60 >= 15
;
题目链接
这个题目,需要我们统计每个日期销售的商品数量和名称
那么毫无疑问,我们需要根据日期对数据进行分组
接着查询每个分组中元素的数量
最后使用group_concat
,可以展示所有的产品名称,需要注意的是,产品名称的展示需要我们进行排序,而在group_concat
中进行排序,就直接在所有内容后面使用group by
就可以了。对了,记得去重
select
sell_date,
count(distinct product) as num_sold,
group_concat(distinct product order by product) as products
from
activities
group by
sell_date
;
题目链接
题目要求我们统计所有在六月和七月各自花费超过100的顾客
不难发现,要求的顾客是六月花费超过100,和七月花费超过100的顾客集合的交集
这里我们可以使用子查询分别查询出在六月和七月花费超过100的顾客在取他们的交集
或者,我们还可以使用另一个思路,通过聚合函数配合case-when来对其中元素进行筛选,进而分别统计出某顾客在七月和六月的花费,这样搞只需要使用一个子查询查询所有的用户id即可。
在这个子查询中,我们需要根据用户分组,然后筛选出所有满足条件的组,并报告他们的id。
select
customer_id,
name
from
customers
where
customer_id in(
select
customer_id
from
orders o
join product p on o.product_id = p.product_id
group by
o.customer_id
having
sum(case when order_date between '2020-06-01' and '2020-06-30' then quantity * price else 0 end) >= 100
and
sum(case when order_date between '2020-07-01' and '2020-07-31' then quantity * price else 0 end) >= 100
)
;
题目链接
题目需要我们修复所有的产品名称,需要去掉名称中前后的空格,并将所有的字母变成小写
这个题实际上是一个考验字符串处理的题目。
将大小写统一可以使用lower
和upper
函数来完成。
去掉字符串中的前后空格可以使用trim
函数来完成
使用这两个函数,就可以完成本题的需求了。
select
product_name,
sale_date,
count(*) as total
from
(
select
lower(trim(product_name)) as product_name,
substring(sale_date,1,7) as sale_date
from
sales
) as s
group by
product_name,sale_date
order by
product_name , sale_date
;