leetcode数据库题第五弹
- 1141. 查询近30天活跃用户数
- 1148. 文章浏览 I
- 1158. 市场分析 I
- 1164. 指定日期的产品价格
- 1174. 即时食物配送 II
- 1179. 重新格式化部门表
- 1193. 每月交易 I
- 1204. 最后一个能进入电梯的人
- 1211. 查询结果的质量和占比
- 1251. 平均售价
- 小结
1141. 查询近30天活跃用户数
https://leetcode.cn/problems/user-activity-for-the-past-30-days-i/
简单题目,统计每天活跃的用户,有的用户会跨天,则两天都算活跃用户,也就是忽略状态,有操作就算。
# oracle 因为日期不包含时间,所以 oracle 还要转一下数据类型
select to_char(activity_date,'YYYY-mm-DD') day,count(distinct user_id) active_users
from activity
where activity_date between '2019-6-28' and '2019-7-27'
group by activity_date
# mssql && mysql
select activity_date day,count(distinct user_id) active_users
from activity
where activity_date between '2019-6-28' and '2019-7-27'
group by activity_date
CSDN 文盲老顾的博客,https://blog.csdn.net/superwfei
1148. 文章浏览 I
https://leetcode.cn/problems/article-views-i/
嗯。。。。。排重 distinct ?注意输出信息有要求排序。
select distinct author_id id
from views
where author_id=viewer_id
order by author_id
1158. 市场分析 I
https://leetcode.cn/problems/market-analysis-i/
类型转换函数,日期函数要熟练掌握。因为要列出订单数为0的用户,所以应该先统计后关联。然后为空的数据,就要用到各自的 nvl、isnull、ifnull 了,注意不同数据库有不同的函数和用法。
#oracle
select u.user_id buyer_id,to_char(join_date,'YYYY-mm-DD') join_date,nvl(orders_in_2019,0) orders_in_2019
from users u
left join (
select buyer_id,count(order_id) orders_in_2019
from orders
where order_date>='2019-01-01' and order_date<'2020-01-01'
group by buyer_id
) c on u.user_id=c.buyer_id
# mssql
select u.user_id buyer_id,join_date,isnull(orders_in_2019,0) orders_in_2019
from users u
left join (
select buyer_id,count(order_id) orders_in_2019
from orders
where order_date>='2019-01-01' and order_date<'2020-01-01'
group by buyer_id
) c on u.user_id=c.buyer_id
# mysql
select u.user_id buyer_id,join_date,ifnull(orders_in_2019,0) orders_in_2019
from users u
left join (
select buyer_id,count(order_id) orders_in_2019
from orders
where order_date>='2019-01-01' and order_date<'2020-01-01'
group by buyer_id
) c on u.user_id=c.buyer_id
1164. 指定日期的产品价格
https://leetcode.cn/problems/product-price-at-a-given-date/
用两个子查询即可,一个取得所有产品 id ,并给出默认价格,另一个获取截止日期前最后一次更新的价格,如果没有更新的,就按默认价格,否则按更新价格列出。
# mysql
select a.product_id,ifnull(b.new_price,a.price) price
from (
select distinct product_id,10 price
from products
) a
left join (
select * from (
select *,row_number() over(partition by product_id order by change_date desc) rid
from products
where change_date<='2019-08-16'
) x
where rid=1
) b on a.product_id=b.product_id
# mssql
select a.product_id,isnull(b.new_price,a.price) price
from (
select distinct product_id,10 price
from products
) a
left join (
select * from (
select *,row_number() over(partition by product_id order by change_date desc) rid
from products
where change_date<='2019-08-16'
) x
where rid=1
) b on a.product_id=b.product_id
# oracle
select a.product_id,nvl(b.new_price,a.price) price
from (
select distinct product_id,10 as price
from products
) a
left join (
select * from (
select p.*,row_number() over(partition by product_id order by change_date desc) rid
from products p
where change_date<='2019-08-16'
) x
where x.rid=1
) b on a.product_id=b.product_id
1174. 即时食物配送 II
https://leetcode.cn/problems/immediate-food-delivery-ii/
额。。。sum 聚合函数立大功。因为 mssql 需要整型除整型,最后还会得到整型,所以,中间懒得转类型,直接乘一个浮点型就当转类型了。
# oracle && mysql
select round(sum(case when order_date=customer_pref_delivery_date then 1 else 0 end) / count(0) * 100,2) immediate_percentage
from (
select d.*,row_number() over(partition by customer_id order by order_date) rid
from delivery d
) a
where rid=1
# mssql
select convert(decimal(5,2),sum(case when order_date=customer_pref_delivery_date then 1 else 0 end) * 1.0 / count(0) * 100) immediate_percentage
from (
select d.*,row_number() over(partition by customer_id order by order_date) rid
from delivery d
) a
where rid=1
1179. 重新格式化部门表
https://leetcode.cn/problems/reformat-department-table/
哦吼,一个经典行转列的需求。
# 经典行转列,一拖三
select id,max(case when month='Jan' then revenue else null end) Jan_Revenue
,max(case when month='Feb' then revenue else null end) Feb_Revenue
,max(case when month='Mar' then revenue else null end) Mar_Revenue
,max(case when month='Apr' then revenue else null end) Apr_Revenue
,max(case when month='May' then revenue else null end) May_Revenue
,max(case when month='Jun' then revenue else null end) Jun_Revenue
,max(case when month='Jul' then revenue else null end) Jul_Revenue
,max(case when month='Aug' then revenue else null end) Aug_Revenue
,max(case when month='Sep' then revenue else null end) Sep_Revenue
,max(case when month='Oct' then revenue else null end) Oct_Revenue
,max(case when month='Nov' then revenue else null end) Nov_Revenue
,max(case when month='Dec' then revenue else null end) Dec_Revenue
from department
group by id
# mssql 特有行转列
select id,Jan Jan_Revenue,Feb Feb_Revenue,Mar Mar_Revenue,Apr Apr_Revenue,May May_Revenue,Jun Jun_Revenue,Jul Jul_Revenue,Aug Aug_Revenue,Sep Sep_Revenue,Oct Oct_Revenue,Nov Nov_Revenue,Dec Dec_Revenue
from department d
pivot(max(revenue) for month in (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)) p
1193. 每月交易 I
https://leetcode.cn/problems/monthly-transactions-i/
嗯。。。。日期转字符串并截取,然后聚合加 case when 。
# mssql
select convert(varchar(7),trans_date,126) month,country,count(0) trans_count
,sum(case when state='approved' then 1 else 0 end) approved_count
,sum(amount) trans_total_amount,sum(case when state='approved' then amount else 0 end) approved_total_amount
from transactions
group by convert(varchar(7),trans_date,126),country
# oracle
select to_char(trans_date,'YYYY-mm') month,country,count(0) trans_count
,sum(case when state='approved' then 1 else 0 end) approved_count
,sum(amount) trans_total_amount,sum(case when state='approved' then amount else 0 end) approved_total_amount
from transactions
group by to_char(trans_date,'YYYY-mm'),country
# mysql
select DATE_FORMAT(trans_date,'%Y-%m') month,country,count(0) trans_count
,sum(case when state='approved' then 1 else 0 end) approved_count
,sum(amount) trans_total_amount,sum(case when state='approved' then amount else 0 end) approved_total_amount
from transactions
group by DATE_FORMAT(trans_date,'%Y-%m'),country
然后,测试了一下,可以建立索引,下次碰到有超时风险的情况,就用索引来优化一下好了。
#mssql
if not exists(select * from sysindexes where name='NonClusteredIndex-20230619-125842')
begin
CREATE NONCLUSTERED INDEX [NonClusteredIndex-20230619-125842] ON transactions
(
id ASC
)
INCLUDE ( country, trans_date) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
end
select convert(varchar(7),trans_date,126) month,country,count(0) trans_count
,sum(case when state='approved' then 1 else 0 end) approved_count
,sum(amount) trans_total_amount,sum(case when state='approved' then amount else 0 end) approved_total_amount
from transactions
group by convert(varchar(7),trans_date,126),country
1204. 最后一个能进入电梯的人
https://leetcode.cn/problems/last-person-to-fit-in-the-bus/
额,刚分享过的知识点,聚合函数也可以和开窗函数一起使用,嘿嘿。
由于 oracle 的 order 不能直接影响当前 rownum,所以还要套一层,真麻烦
# mssql
select top 1 person_name
from (
select *,sum(weight) over(order by turn) w
from queue
) a
where w<=1000
order by w desc
# mysql
select person_name
from (
select *,sum(weight) over(order by turn) w
from queue
) a
where w<=1000
order by w desc
limit 0,1
# oracle
select * from (
select person_name
from (
select q.*,sum(weight) over(order by turn) w
from queue q
) a
where w<=1000
order by w desc
) a
where rownum=1
1211. 查询结果的质量和占比
https://leetcode.cn/problems/queries-quality-and-percentage/
嗯。。。计算后的结果求平均,以及部分符合条件的数据求汇总,sum 和 case 组合永远好用。额。。。注意百分比那个需要乘100。
# oracle && mysql
select query_name,round(avg(rating * 1.0 / position),2) quality
,round(sum(case when rating < 3 then 1.0 else 0 end) / count(0) * 100,2) poor_query_percentage
from queries
group by query_name
# mssql
select query_name,convert(decimal(5,2),avg(rating * 1.0 / position)) quality
,convert(decimal(5,2),sum(case when rating < 3 then 1.0 else 0 end) / count(0) * 100) poor_query_percentage
from queries
group by query_name
1251. 平均售价
https://leetcode.cn/problems/average-selling-price/
临到本期结束,又一个简单题目,直接聚合比聚合即可。
# mssql
select u.product_id,convert(decimal(8,2),sum(u.units * 1.0 * p.price) / sum(u.units)) average_price
from unitssold u
left join prices p on u.purchase_date between p.start_date and p.end_date and u.product_id=p.product_id
group by u.product_id
# mysql && oracle
select u.product_id,round(sum(u.units * 1.0 * p.price) / sum(u.units),2) average_price
from unitssold u
left join prices p on u.purchase_date between p.start_date and p.end_date and u.product_id=p.product_id
group by u.product_id
小结
本次题目,多余集中到了开窗函数和分组上,结合各种子查询,很容易得到结果。
注意审题,输出信息有时会有要求。注意子查询关联优先级,包含空值时,注意关联方式即可。
行转列文章,可以参考老顾之前的:【新星计划】数据库行列转换初识
聚合函数和开窗函数,可以参考老顾之前的:【新星计划】数据库 排名函数 初识
可惜的是老顾没有力扣会员,也不明白,这些所谓的困难题目为什么要会员才能刷,真想进去玩玩看啊。