文章目录
【LeetCode高频SQL50题-基础版】打卡第3天:第16~20题
⛅前言
平均售价
🔒题目
🔑题解
考查知识点:sum()
、ifnull()
sum(column)
:对表中某一列进行求和round(number, n)
:对number保留n位小数,采用四舍五入的方式ifnull(expression_1, expression_2)
:如果表达式1为null,则结果为expression_2,如果表达式不为null,则结果为expression_1- 对 null 值的处理
这里我选择采用一步一步调整SQL的方式来解题,因为这种方式能够让我们更加清晰的了解SQL的执行过程
1)先做一个左连接
select *
from Prices p left join UnitsSold u on p.product_id = u.product_id;
| product_id | start_date | end_date | price | product_id | purchase_date | units |
| ---------- | ---------- | ---------- | ----- | ---------- | ------------- | ----- |
| 1 | 2019-02-17 | 2019-02-28 | 5 | 1 | 2019-03-01 | 15 |
| 1 | 2019-02-17 | 2019-02-28 | 5 | 1 | 2019-02-25 | 100 |
| 1 | 2019-03-01 | 2019-03-22 | 20 | 1 | 2019-03-01 | 15 |
| 1 | 2019-03-01 | 2019-03-22 | 20 | 1 | 2019-02-25 | 100 |
| 2 | 2019-02-01 | 2019-02-20 | 15 | 2 | 2019-03-22 | 30 |
| 2 | 2019-02-01 | 2019-02-20 | 15 | 2 | 2019-02-10 | 200 |
| 2 | 2019-02-21 | 2019-03-31 | 30 | 2 | 2019-03-22 | 30 |
| 2 | 2019-02-21 | 2019-03-31 | 30 | 2 | 2019-02-10 | 200 |
2)从上面的执行结果来看,producet_id 发生了重复,我们采用 group by进行去重
select *
from Prices p left join UnitsSold u on p.product_id = u.product_id
group by p.product_id;
| product_id | start_date | end_date | price | product_id | purchase_date | units |
| ---------- | ---------- | ---------- | ----- | ---------- | ------------- | ----- |
| 1 | 2019-02-17 | 2019-02-28 | 5 | 1 | 2019-03-01 | 15 |
| 2 | 2019-02-01 | 2019-02-20 | 15 | 2 | 2019-03-22 | 30 |
3)我们还需要通过按照产品销售日期计算每一个产品的总销售额
select p.product_id, SUM(units * price) as all_price, SUM(units) as all_units
from Prices p left join UnitsSold u on p.product_id = u.product_id
where u.purchase_date between p.start_date and p.end_date
group by p.product_id;
| product_id | all_price | all_units |
| ---------- | --------- | --------- |
| 1 | 800 | 115 |
| 2 | 3900 | 230 |
product_id = 1 的计算结果是 800=(100 * 5)+(15 * 20),115=100+15,
4)进行相除,计算平均售价
select p.product_id, round(sum(units * price) / sum(units),2) as average_price
from Prices p left join UnitsSold u on p.product_id = u.product_id
where u.purchase_date between p.start_date and p.end_date
group by p.product_id;
| product_id | average_price |
| ---------- | ------------- |
| 1 | 6.96 |
| 2 | 16.96 |
5)对于销售额为 null 的商品采用上面的SQL会报错,因为null与其它结果参数运算的结果可能为null,而我们需要的结果是0
select p.product_id, round(ifnull(sum(units * price) / sum(units), 0), 2) as average_price
from Prices p left join UnitsSold u on p.product_id = u.product_id
where u.purchase_date between p.start_date and p.end_date or u.product_id is null
group by p.product_id;
注意:
- null与其它数值类型进行运算,结果为null,所以要通过
ifnull()
函数计算过滤 - null不参与聚合函数的计算,为了保障
sum()
函数的求和数量正确需要添加一个条件 or u.product_id is null ,把 UnitsSold表中 null 的列也统计起来,这样记录总数才是正确的,否则记录总数会比真实值小
项目员工I
🔒题目
🔑题解
- 考察知识点:
左连接
、group by
、sum
、count
、round
1)先进行左连接
select *
from Project p left join Employee e on p.employee_id = e.employee_id;
| project_id | employee_id | employee_id | name | experience_years |
| ---------- | ----------- | ----------- | ------ | ---------------- |
| 1 | 1 | 1 | Khaled | 3 |
| 1 | 2 | 2 | Ali | 2 |
| 1 | 3 | 3 | John | 1 |
| 2 | 1 | 1 | Khaled | 3 |
| 2 | 4 | 4 | Doe | 2 |
2)和目标结果表进行对比,发现还需要对 project_id 进行去重
select *
from Project p left join Employee e on p.employee_id = e.employee_id
group by p.project_id;
| project_id | employee_id | employee_id | name | experience_years |
| ---------- | ----------- | ----------- | ------ | ---------------- |
| 1 | 1 | 1 | Khaled | 3 |
| 2 | 1 | 1 | Khaled | 3 |
3)和目标结果表进行对比,发现还需要计算 每一个项目的员工的平均年限,同时需要使用round()
函数对平均值进行保留两位小数
一个项目员工的平均年限=项目所有员工的年限/项目员工的数量
select p.project_id, round(sum(e.experience_years)/count(p.project_id), 2) average_years
from Project p left join Employee e on p.employee_id = e.employee_id
group by p.project_id;
+-------------+---------------+
| project_id | average_years |
+-------------+---------------+
| 1 | 2.00 |
| 2 | 2.50 |
+-------------+---------------+
各赛事的用户注册率
🔒题目
🔑题解
- 考察知识点:
count
、round
、group by
、order by
select contest_id , round(count(user_id) * 100/ (select count(*) from users), 2) percentage
from Register
group by contest_id
order by percentage desc, contest_id
查询结果的质量和占比
🔒题目
🔑题解
-
考察知识点:
count
、round
、group by
、sum
、if
、avg
-
方式一:
select query_name, round((sum(rating/position)/count(query_name)), 2) quality, round(sum(if(rating < 3, 1, 0)) * 100 / count(*), 2) poor_query_percentage from Queries group by query_name;
其中
if(rating < 3, 1, 0)
等价于case when rating < 3 then 1 else 0 end
-
方式二:
select query_name, round(avg(rating/position), 2) quality, round(avg(rating < 3)*100, 2) poor_query_percentage from Queries group by query_name;
其中
avg(rating < 3)
这个表达式的含义是,计算评分小于3占总数的百分比
备注:这两个SQL,第二个适用 avg
函数的SQL性能更好,因为第一个 SQL 查询中使用了多个聚合函数和条件语句,如 sum
、round
、if
等,这可能会导致更多的计算和操作,从而影响查询的性能。而第二个 SQL 查询中只使用了两个简单的聚合函数 avg
和 round
,没有使用条件语句,所以计算的复杂度较低,查询性能相对较好。
每月交易I
🔒题目
🔑题解
- 考察知识点:
count
、sum
、data_formate
、group by
、order by
select
date_format(trans_date, "%Y-%m") month,
country,
count(*) trans_count,
count(if(state='approved', 1, null)) approved_count,
sum(amount) trans_total_amount,
sum(if(state = 'approved', amount, 0)) approved_total_amount
from Transactions
group by country, month
order by month;
还可以换一种写法
select
left(trans_date, 7) month,
country,
count(*) trans_count,
sum(if(state='approved', 1, 0)) approved_count,
sum(amount) trans_total_amount,
sum(if(state = 'approved', amount, 0)) approved_total_amount
from Transactions
group by country, month
order by month;