LeetCode的高频SQL50题(基础版)学习笔记
题目在此网站 https://leetcode.cn/
查询
# Write your MySQL query statement below
select product_id
from products
where low_fats like 'Y'
and recyclable like 'Y';
# Write your MySQL query statement below
select name
from customer
where referee_id !=2
or referee_id is null;
# Write your MySQL query statement below
select name,population,area
from world
where population >= 25000000
or area >= 3000000;
# Write your MySQL query statement below
select distinct(author_id) as id
from views
where author_id = viewer_id
order by id asc;
# Write your MySQL query statement below
select tweet_id
from tweets
where char_length(content) > 15;
char_length() : 求出字符的长度
为什么不用length()呢 length是求字节长度的,
length(str)
(1)计算单位:字节
(2)utf8编码:一个汉字三个字节,一个数字或字母一个字节。
(3)gbk编码:一个汉字两个字节,一个数字或字母一个字节。
所以用length() 遇到汉字的话 不能准确计算字符的个数
连接
# Write your MySQL query statement below
select unique_id,name
from employees es
left join EmployeeUNI eu
on es.id = eu.id;
# Write your MySQL query statement below
select p.product_name,s.year,s.price
from sales s
left join product p
on s.product_id = p.product_id
# Write your MySQL query statement below
select distinct(customer_id),count(customer_id) as count_no_trans
from visits v
left join transactions t
on v.visit_id = t.visit_id
where transaction_id is null
group by customer_id
# Write your MySQL query statement below
select w1.id as id
from weather w1,weather w2
where datediff(w1.recordDate,w2.recordDate)=1
and w1.temperature > w2.temperature
datediff是返回两个日期的差 datediff(a,b) = a-b 所以昨天和今天差一天 = 1
# Write your MySQL query statement below
select machine_id,
round(sum(case when activity_type = 'end' then timestamp else - timestamp end)/count(distinct process_id),3) as processing_time
from activity
group by machine_id
对机械id进行分组,从而计算每个机械id的平均耗时,round(xxxxx,3)是保留三位小数
sum(case when activity_type = ‘end’ then timestamp else -timestamp)
因为是结束的时间戳减去开始的时间戳 所以结束时的时间戳是正的,开始的时间戳是负的。
最终求和是执行所有任务的执行时间
现在还差执行了几个任务的数据,count(distinct process_id) 就是求出执行了几个任务
# Write your MySQL query statement below
select e.name as name,b.bonus as bonus
from employee e
left join bonus b
on e.empId = b.empId
where bonus is null
or bonus < 1000
# Write your MySQL query statement below
select stu.student_id as student_id,
stu.student_name as student_name,
sub.subject_name as subject_name,
count(e.subject_name) as attended_exams
from students stu
cross join subjects sub
left join Examinations e
on stu.student_id = e.student_id
and e.subject_name = sub.subject_name
group by stu.student_id,sub.subject_name
order by stu.student_id,sub.subject_name;
学生表与科目表交叉连接 产生两个表的笛卡尔积 因为每个学生都对应的科目都是一样的
再左连接这两个表的笛卡尔积 用student_id相连 并且 用subject_name 相连
因为要求出来每个学生的每个科目的测试次数 所以用student_id,subject_name 分组
排序也是一样。
# Write your MySQL query statement below
select name
from employee
where id
in(
select managerId
from employee
group by managerId
having count(id) >= 5)
首先子查询当中 找出来有至少5名下属经理的id 然后从父查询根据id找到经理的名字
# Write your MySQL query statement below
select s.user_id as user_id,
round(ifnull(sum(case when action="confirmed" then 1 else 0 end)/count(action),0),2) as confirmation_rate
from signups s
left join confirmations c
on s.user_id = c.user_id
group by s.user_id;
round(,2) 保留两位小数 ifnull(,0) 如果是null 结果为0
求平均率(确认的行为/总的行为)所以 用到了
sum(case when action="confirmed" then 1 else 0 end)/count(action)
聚合函数
# Write your MySQL query statement below
select *
from cinema
where description not like 'boring'
and id%2=1
order by rating desc
# Write your MySQL query statement below
select p.product_id,
round(ifnull(sum(u.units*p.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 end_date or u.product_id is null
group by p.product_id
产品表与销售表相连接 因为每个时间段的产品售价都不一样
所以 where u.purchase_date between p.start_date and end_date
u.product_id is null 是因为有产品没有被购买 所以销售表没有销售数据 但是也要显示商品的平均售价
round(,2) 保留2位小数
ifnull(,0) 解决销售表为null的问题,如果为null,默认为0
平均售价 (不同时间的价格*不同时间的销量+不同时间的价格*不同时间的销量+....)/总销量
所以转为sql就是 sum(u.units*p.price)/sum(units)
# Write your MySQL query statement below
select p.project_id,
round(avg(e.experience_years),2) as average_years
from project p
left join employee e
on e.employee_id = p.employee_id
group by p.project_id
# Write your MySQL query statement below
select contest_id,
round(count(user_id)/(select count(*) from users)*100,2) as percentage
from register
group by contest_id
order by percentage desc , contest_id asc
round(,2)是保留两位小数,
因为以百分率显示,所以分母得称上100
分母统计出所有的用户人数
分子统计出注册赛事的用户人数
然后对每个赛事分组
求出每个赛事的用户注册率
按照 percentage 降序 desc ,contest_id 升序 asc 可省略
# Write your MySQL query statement below
select query_name,
round(avg(rating/position),2) as quality,
round(100*avg(rating<3),2) as poor_query_percentage
from queries
group by query_name
having query_name is not null;
query_name中 可能会有为名字为空的用户 所以用having query_name is not null筛选出来
avg(条件)相当于sum(if(条件,1,0))/count(全体)
进阶
sum(if(条件,N,0))/count(全体) 可用 N*avg(条件)代替
使用bool条件将多个样本判断为0和1,多个0和多个1的平均值就是1在整体中的比例,也即满足条件的样本在整体中的比例。
# Write your MySQL query statement below
select left(trans_date,7) as month,
country,
count(*) as trans_count,
sum(case when state = 'approved' then 1 else 0 end) as approved_count,
sum(amount) as trans_total_amount,
sum(if(state='approved',amount,0)) as approved_total_amount
from transactions
group by month,country;
首先是查找每个月和每个国家的数据 所以对month,country分组
left(,7)从左边数截取7个字符
if(条件,真,假)
# Write your MySQL query statement below
select round(sum(order_date=customer_pref_delivery_date)/count(*)*100,2) as immediate_percentage
from delivery
where (customer_id,order_date) in(
select customer_id,min(order_date)
from delivery
group by customer_id);
子查询当中 找出每个顾客的首次订单,将顾客id和首次订单日期传入父查询当中
然后父查询 求 即时订单率(即时订单数/总订单数)
即时订单(下单日期与期望配送日期同一天)
所以sum(order_date=customer_pref_delivery_date) 找出所有的即时订单
# Write your MySQL query statement below
select round(avg(a.event_date is not null),2) as fraction
from (
select player_id, min(event_date) as login
from Activity
group by player_id
) p
left join Activity a
on p.player_id=a.player_id and datediff(a.event_date,p.login)=1
子查询当中 是查询每个用户的首次登录日期
与原表连接 条件为 首次登录日期和转天都有登录
所以datediff(a.event_date,p.login)=1 找出这两天都有登录的用户
avg(a.event_date is not null) 找出结束日期不为空的。
排序和分组
# Write your MySQL query statement below
select teacher_id,count(distinct subject_id) as cnt
from teacher
group by teacher_id;
因为大学老师 同一个科目会有多门课 用count(distinct subject_id) 来统计科目种类的数量
# Write your MySQL query statement below
select activity_date as day ,
count(distinct user_id) as active_users
from activity
where activity_date between '2019-06-28' and '2019-07-27'
group by activity_date
where activity_date between '2019-06-28' and '2019-07-27' 查询30天内
group by activity_date 每天活跃的用户数
count(distinct user_id) as active_users 因为用户每天可能会有多个记录 所以得用distinct 保留一个
# Write your MySQL query statement below
select product_id,product_name
from sales
natural join product
group by product_id
having max(sale_date) <= '2019-03-31' and min(sale_date) >= '2019-01-01';
NATURAL JOIN 是 SQL 中的一个连接操作,它基于两个表中名称相同的列进行连接。在执行
NATURAL JOIN 时,SQL 会自动查找两个表中名称相同的列,并使用这些列作为连接条件。
对每个产品分组
如果最大日期和最小日期 在1-3月之间 证明这个产品只在这个日期内销售
# Write your MySQL query statement below
select class
from courses
group by class
having count(*) >= 5;
# Write your MySQL query statement below
select user_id,
count(user_id) as followers_count
from Followers
group by user_id
order by user_id asc
# Write your MySQL query statement below
select if(count(num)=1,num,null) as num
from MyNumbers
group by num
order by count(num) asc ,num desc
limit 1;
group by num 对数字分组
order by count(num) asc ,num desc 对每组数字进行计数,按每组数量升序排序,如果相同,再按数字大小降序排序 limit 1 取第一个
if(count(num)=1,num,null) 如果这组是一个数,就输入它,多个数返回null
# Write your MySQL query statement below
select Customer_id
from Customer
group by Customer_id
having count(distinct product_key) =
(select count(*) from product)
子查询当中 统计了产品的总个数
然后对每个用户购买的去重产品统计 如果个数相同则购买了全部商品
高级查询和连接
# Write your MySQL query statement below
select e1.employee_id as employee_id,
e1.name as name,
count(e2.employee_id) as reports_count,
round(avg(e2.age),0) as average_age
from employees e1
left join employees e2
on e1.employee_id = e2.reports_to
where e2.employee_id is not null
group by e1.employee_id
order by e1.employee_id
两张Employees表a和b自连接(经理至少有一位下属员工,自连接可以直接过滤出有下属员工的经理。如果用外连接,表中会包含没有下属员工的经理,需要用where语句再次过滤),a表信息作为经理,b表信息作为下属员工
对经理表的employee_id进行分组,计算出下属员工人数和平均员工年龄
# Write your MySQL query statement below
select employee_id,department_id
from Employee
where primary_flag = 'Y' or employee_id in(
select employee_id
from Employee
group by employee_id
having count(employee_id) = 1
)
子查询的作用就是找出员工只在一个部门的员工id
primary_flag为Y的 这两个条件能够找出所有的员工的直属部门
# Write your MySQL query statement below
select
x,
y,
z,
case when x<y+z and y<z+x and z<x+y then 'Yes' else 'No' end as triangle
from triangle
组成三角形的条件:前两条边的和必须大于第三条边
# Write your MySQL query statement below
select distinct l1.num as ConsecutiveNums
from logs l1,logs l2,logs l3
where l1.id = l2.id+1
and l2.id = l3.id+1
and l1.num = l2.num
and l2.num = l3.num;
自链接三个表 l1代表第一个 ,l2代表第二个,l3代表第三个 并且这三次的数相等
然后去重 找到连续出现3次的数字
# Write your MySQL query statement below
select distinct p1.product_id, ifnull(res.new_price, 10) as price
from Products p1
left join
(select product_id, new_price, change_date
from (select product_id,
new_price,
change_date,
row_number() over (partition by product_id order by change_date desc) num
from Products
where change_date <= '2019-08-16') t
where t.num = 1) res
on p1.product_id = res.product_id
# Write your MySQL query statement below
select person_name
from (
select turn,person_id,person_name,weight,sum(weight) over(order by turn asc) as total_weight
from queue
order by turn asc
) q1
where total_weight<=1000
order by total_weight desc
limit 1;
# Write your MySQL query statement below
select 'Low Salary' as category,count(*)as accounts_count from accounts where income<20000
union
select 'Average Salary' as category,count(*) as accounts_count from accounts where income >=20000 and income <= 50000
union
select 'High Salary' as category ,count(*) as accounts_count from accounts where income > 50000;
# Write your MySQL query statement below
select employee_id
from employees
where salary < 30000
and manager_id not in(
select employee_id
from employees
)
order by employee_id;
# Write your MySQL query statement below
select if(id%2=0,id-1,if(id=(select count(distinct id)from seat),id,id+1)) as id,
student
from seat
order by id;
思路:偶数-1 奇数+1 如果总数是奇数 最后一位不变
所以if判断 是否偶数 如果是 id-1 不是再判断 是否为最后一位 如果是最后一位就不变,不是最后一位+1
偶数不用判断是否为最后一位 因为最后一位是偶数 学生总数也会是偶数
# Write your MySQL query statement below
(
select u.name as results
from movieRating m
left join users u
on u.user_id = m.user_id
group by m.user_id
order by count(movie_id) desc,u.name asc
limit 1
)
union all
(
select mv.title as results
from movieRating mr
left join movies mv
on mr.movie_id = mv.movie_id
where date_format(mr.created_at,'%Y-%m') = '2020-02'
group by mv.movie_id
order by avg(mr.rating) desc,mv.title asc
limit 1
)
# Write your MySQL query statement below
select distinct visited_on,
sum_amount as amount,
round(sum_amount/7,2) as average_amount
from (
select visited_on,
sum(amount) over(order by visited_on rows 6 preceding ) as sum_amount
from (
select visited_on,
sum(amount) as amount
from customer
group by visited_on
) TT
) LL
where datediff(visited_on,(select min(visited_on) from customer)) >= 6;
首先还是简要地理解一下窗口函数该怎么写,不过这次要多加一点点东西
[你要的操作] OVER ( PARTITION BY <用于分组的列名>
ORDER BY <按序叠加的列名>
ROWS <窗口滑动的数据范围> )
<窗口滑动的数据范围> 用来限定[ 你要的操作] 所运用的数据的范围,具体有如下这些:
当前行 - current row
之前的行 - preceding
之后的行 - following
无界限 - unbounded
表示从前面的起点 - unbounded preceding
表示到后面的终点 - unbounded following
举例理解一下:
取当前行和前五行:ROWS between 5 preceding and current row --共6行
取当前行和后五行:ROWS between current row and 5 following --共6行
取前五行和后五行:ROWS between 5 preceding and 5 folowing --共11行
本题中,要的是 按日期的, 金额的累计, 从当天算起共7天
则可理解成 我要的操作是‘累计金额’, 按序叠加的列是‘日期’, 窗口内的数据要‘取当前行和前6行’
不过即使前边的数据不够,窗口函数也会将范围内的数据框住并计算,因此需要最后手动地只要能够完整框住7天*的情况 【绊子1】
另外比较阴损的是,本题的数据中存在着同一用户在某日多次消费的情况,这样一来即使窗口照旧向前取6天就无法覆盖被挤出去的数据了,因此,需要构建一个小表格用来存放每天的金额总量 【绊子2】
# Write your MySQL query statement below
select id,
count(*) as num
from (
select requester_id as id from RequestAccepted
union all
select accepter_id as id from RequestAccepted
) t1
group by id
order by num desc
limit 1;
简单来讲就是统计某个id在requester_id和accepter_id中出现的次数和。
直接使用union会合并重复数据,因此这里使用union all。
统计后排序再取表中第一项即可。
# Write your MySQL query statement below
select
round(sum(tiv_2016),2) as tiv_2016
from (
select
tiv_2016,
count(*)over(partition by tiv_2015) as cn_2015,
count(*)over(partition by lat,lon) cn_l
from insurance
) t
where cn_2015 > 1 and cn_l = 1
# Write your MySQL query statement below
select
department.name as department,
e1.name as employee,
e1.salary as salary
from employee as e1,department
where e1.departmentId = department.id
and 3 > (
select count(distinct e2.salary)
from employee as e2
where e1.salary < e2.salary
and e1.departmentId = e2.departmentId
)
order by department asc,salary desc;
高级字符串函数、正则表达式、子句
# Write your MySQL query statement below
select user_id,
concat(upper(left(name,1)),lower(substring(name,2))) as name
from users
order by user_id asc;
# Write your MySQL query statement below
# 一、计算字段
# 其实本题主要考察的就是计算字段的使用。
# 二、知识点
# 2.1 CONCAT() 函数
# CONCAT 可以将多个字符串拼接在一起。
# 2.2 LEFT(str, length) 函数
# 从左开始截取字符串,length 是截取的长度。
# 2.3 UPPER(str) 与 LOWER(str)
# UPPER(str) 将字符串中所有字符转为大写
# LOWER(str) 将字符串中所有字符转为小写
# 2.4 SUBSTRING(str, begin, end)
# 截取字符串,end 不写默认为空。
# SUBSTRING(name, 2) 从第二个截取到末尾,注意并不是下标,就是第二个。
# CONCAT 用来拼接字符串 ● LEFT 从左边截取字符 ● RIGHT 从右边截取字符 ● UPPER 变为大写 ● LOWER 变为小写 ● LENGTH 获取字符串长度
# Write your MySQL query statement below
select *
from patients
where conditions regexp '^DIAB1|\\sDIAB1';
'^DIAB1|\\sDIAB1'
匹配以DIAB1开头的或 空格+DIAB1
# Write your MySQL query statement below
delete p1
from person p1,person p2
where p1.email = p2.email and p1.id > p2.id;
where p1.email = p2.email and p1.id > p2.id;
找到 相同的邮箱名 并且 删除id较大的
# Write your MySQL query statement below
select ifnull(
(select distinct salary as SecondHighestSalary
from employee
order by salary desc
limit 1,1)
,null) as SecondHighestSalary;
# Write your MySQL query statement below
select sell_date,
count(distinct product) as num_sold,
group_concat(
distinct product
order by product asc
separator ','
) as products
from activities
group by sell_date
order by sell_date;
# Write your MySQL query statement below
select p.product_name,
sum(o.unit) as unit
from orders o
left join products p
on o.product_id = p.product_id
where o.order_date like '2020-02%'
group by p.product_name
having unit >= 100
# Write your MySQL query statement below
select *
from users
where mail regexp '^[a-zA-Z][a-zA-Z0-9_.-]*\\@leetcode\\.com$'