LeetCode的高频SQL50题(基础版)学习笔记

题目在此网站 https://leetcode.cn/

查询

sql 复制代码
# Write your MySQL query statement below
select product_id
from products
where low_fats like 'Y'
and recyclable like 'Y';
SQL 复制代码
# Write your MySQL query statement below
select name
from customer
where referee_id !=2
or referee_id is null;
sql 复制代码
# Write your MySQL query statement below
select name,population,area
from world
where population >= 25000000
or area >= 3000000;
sql 复制代码
# Write your MySQL query statement below
select distinct(author_id) as id
from views
where author_id = viewer_id
order by id asc;
sql 复制代码
# 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() 遇到汉字的话 不能准确计算字符的个数

连接

sql 复制代码
# Write your MySQL query statement below
select unique_id,name
from employees es
left join EmployeeUNI eu
on es.id = eu.id;
sql 复制代码
# 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
sql 复制代码
# 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
sql 复制代码
# 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

sql 复制代码
# 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) 就是求出执行了几个任务

sql 复制代码
# 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
sql 复制代码
# 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 分组

排序也是一样。

sql 复制代码
# 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找到经理的名字

sql 复制代码
# 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)

聚合函数

sql 复制代码
# Write your MySQL query statement below
select *
from cinema
where description not like 'boring'
and id%2=1
order by rating desc
sql 复制代码
# 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)
sql 复制代码
# 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
sql 复制代码
# 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 可省略
sql 复制代码
# 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在整体中的比例,也即满足条件的样本在整体中的比例。
sql 复制代码
# 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(条件,真,假)
sql 复制代码
# 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) 找出所有的即时订单
sql 复制代码
# 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) 找出结束日期不为空的。

排序和分组

sql 复制代码
# 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) 来统计科目种类的数量
sql 复制代码
# 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 保留一个
sql 复制代码
# 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月之间 证明这个产品只在这个日期内销售
sql 复制代码
# Write your MySQL query statement below
select class
from courses
group by class
having count(*) >= 5;
sql 复制代码
# 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
sql 复制代码
# 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
sql 复制代码
# Write your MySQL query statement below
select Customer_id
from Customer 
group by Customer_id 
having count(distinct product_key) =
(select count(*) from product)
子查询当中 统计了产品的总个数 
然后对每个用户购买的去重产品统计 如果个数相同则购买了全部商品

高级查询和连接

sql 复制代码
# 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进行分组,计算出下属员工人数和平均员工年龄
sql 复制代码
# 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的 这两个条件能够找出所有的员工的直属部门
sql 复制代码
# 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
组成三角形的条件:前两条边的和必须大于第三条边
sql 复制代码
# 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次的数字
sql 复制代码
# 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
sql 复制代码
# 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;
sql 复制代码
# 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;
 
sql 复制代码
# 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;
sql 复制代码
# 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
偶数不用判断是否为最后一位 因为最后一位是偶数 学生总数也会是偶数
sql 复制代码
# 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
)
sql 复制代码
# 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】
sql 复制代码
# 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。
统计后排序再取表中第一项即可。
sql 复制代码
# 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

sql 复制代码
# 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;

高级字符串函数、正则表达式、子句

sql 复制代码
# 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 获取字符串长度
sql 复制代码
# Write your MySQL query statement below
select *
from patients
where conditions regexp '^DIAB1|\\sDIAB1';
'^DIAB1|\\sDIAB1'
匹配以DIAB1开头的或 空格+DIAB1
sql 复制代码
# 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较大的
sql 复制代码
# 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;
sql 复制代码
# 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;
sql 复制代码
# 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 
sql 复制代码
# Write your MySQL query statement below
select *
from users
where mail regexp '^[a-zA-Z][a-zA-Z0-9_.-]*\\@leetcode\\.com$'
相关推荐
晴天qt011 分钟前
[mysql]mysql排序和分页
数据库·mysql
handsome2137 分钟前
WSL中使用GPU加速AMBER MD--测试
笔记·学习
Jasonakeke3 小时前
【重学 MySQL】三十四、加密与解密函数
数据库·mysql
一知半解搞开发3 小时前
Mysql系列-索引简介
java·数据库·mysql
__AtYou__3 小时前
Golang | Leetcode Golang题解之第405题数字转换为十六进制数
leetcode·golang·题解
吃着火锅x唱着歌3 小时前
Go语言设计与实现 学习笔记 第七章 内存管理(1)
笔记·学习·golang
~在杰难逃~4 小时前
关于订单信息的Excel数据分析报告
笔记·数据分析·excel·数据分析报告
aabbcc456aa4 小时前
ubuntu安装mysql 8.0忘记root初始密码,如何重新修改密码
linux·mysql·ubuntu
Pluses4 小时前
Datawhale X 李宏毅苹果书 AI夏令营 《深度学习详解》第十九章 ChatGPT
人工智能·笔记·深度学习·学习
cyt涛4 小时前
搜索功能技术方案
mysql·elasticsearch·全文检索·canal·索引·数据同步·搜索