Employee 表包含所有员工信息,每个员工有其对应的 Id, salary 和 department Id。
+----+-------+--------+--------------+| Id | Name | Salary | DepartmentId |+----+-------+--------+--------------+|1| Joe |70000|1||2| Henry |80000|2||3| Sam |60000|2||4| Max |90000|1|+----+-------+--------+--------------+
Department 表包含公司所有部门的信息。
+----+----------+| Id | Name |+----+----------+|1| IT ||2| Sales |+----+----------+
编写一个 SQL 查询,找出每个部门工资最高的员工。例如,根据上述给定的表格,Max 在 IT 部门有最高工资,Henry 在 Sales 部门有最高工资。
+------------+----------+--------+| Department | Employee | Salary |+------------+----------+--------+| IT | Max |90000|| Sales | Henry |80000|+------------+----------+--------+
解题思路
方法一:首先找出每个部门的最高工资,然后再去连接Employee表,找到对应的员工姓名以及金额
SELECT
t.Department
,e1.Name Employee
,e1.Salary
FROM Employee e1
LEFTJOIN(SELECT
d.Id
,d.Name Department
,max(e.Salary) 最高工资
FROM Employee e
LEFTJOIN Department d ON e.DepartmentId = d.Id
groupby1,2) t ON e1.DepartmentId = t.Id AND e1.Salary = t.最高工资
方法二:窗口函数
SELECT
t.Department
,t.Employee
,t.Salary
FROM(SELECT
d1.Name Department
,e1.Name Employee
,e1.Salary
,rank()over(PARTITIONBY e1.DepartmentId ORDERBY e1.Salary DESC)AS r_k
FROM Employee e1
LEFTJOIN Department d1 ON e1.DepartmentId = d1.Id
) t
WHERE r_k =1
row_number()函数
SELECT
t.Department
,t.Employee
,t.Salary
FROM(SELECT
d1.Name Department
,e1.Name Employee
,e1.Salary
,row_number()over(PARTITIONBY e1.DepartmentId ORDERBY e1.Salary DESC)AS r_k
FROM Employee e1
LEFTJOIN Department d1 ON e1.DepartmentId = d1.Id
) t
WHERE r_k =1
dense_rank()函数
SELECT
t.Department
,t.Employee
,t.Salary
FROM(SELECT
d1.Name Department
,e1.Name Employee
,e1.Salary
,dense_rank()over(PARTITIONBY e1.DepartmentId ORDERBY e1.Salary DESC)AS r_k
FROM Employee e1
LEFTJOIN Department d1 ON e1.DepartmentId = d1.Id
) t
WHERE r_k =1
SELECT
t.向上偏移2行 ConsecutiveNums
FROM(SELECT
Num
,LEAD(Num,1,NULL)OVER() 向上偏移1行
,LEAD(Num,2,NULL)OVER() 向上偏移2行
FROM Logs
) t
where t.Num = t.向上偏移1行
AND t.向上偏移1行 = t.向上偏移2行
【每日SQL打卡】DAY3|删除重复的电子邮箱【难度简单】
编写一个 SQL 查询,来删除 Person 表中所有重复的电子邮箱,重复的邮箱里只保留 Id 最小 的那个。
+----+------------------+| Id | Email |+----+------------------+|1| john@example.com||2| bob@example.com||3| john@example.com|+----+------------------+
Id 是这个表的主键。
例如,在运行你的查询语句之后,上面的 Person 表应返回以下几行:
+----+------------------+| Id | Email |+----+------------------+|1| john@example.com||2| bob@example.com|+----+------------------+
提示:
执行 SQL 之后,输出是整个 Person 表。
使用 delete 语句
思路:找出相同Email中较大的id后进行删除
解题思路
方法一:利用窗口函数,可以删除两个以上重复项
deletefrom Person
where Id in(SELECT
Id
FROM(SELECT
Id
,RANK()OVER(PARTITIONBY Email ORDERBY Id asc) r_k
FROM Person
) t
WHERE r_k >=2)
deletefrom Person
where Id notin(SELECT
Email最小Id
FROM(SELECT
Email
,min(Id) Email最小Id
FROM Person
groupby1) t
)
方法二:首先找出具有重复的Email后,在找出不是最小id的ID后删除
deletefrom Person
where Id in(SELECT
Id
FROM Person
JOIN(SELECT
Email
,Email最小Id
FROM(SELECT
Email
,min(Id) Email最小Id
,count(*) 次数
FROM Person
groupby1) t
WHERE 次数 >=2) P2 ON Person.Email = P2.Email
WHERE Person.Id != P2.Email最小Id
)
delete 表1,表2(删除哪个表写哪个表的别名)
from 表1inner|left|right|fulljoin 表2on 连接条件
where 筛选条件
SELECT
player_id
,min(event_date) first_login
FROM Activity
GROUPBY1
【每日SQL打卡】DAY 4丨员工薪水中位数【难度困难】
Employee 表包含所有员工。Employee 表有三列:员工Id,公司名和薪水。
+-----+------------+--------+|Id | Company | Salary |+-----+------------+--------+|1| A |2341||2| A |341||3| A |15||4| A |15314||5| A |451||6| A |513||7| B |15||8| B |13||9| B |1154||10| B |1345||11| B |1221||12| B |234||13| C |2345||14| C |2645||15| C |2645||16| C |2652||17| C |65|+-----+------------+--------+
+-----+------------+--------+|Id | Company | Salary |+-----+------------+--------+|5| A |451||6| A |513||12| B |234||9| B |1154||14| C |2645|+-----+------------+--------+
SELECT
Id
,Company
,Salary
FROM(SELECT
Id
,Company
,Salary
,row_number()over(PARTITIONBY Company ORDERBY Salary,Id desc) rnt_asc
,row_number()over(PARTITIONBY Company ORDERBY Salary desc,Id) rnt_desc
FROM Employee
) t
where abs(cast(rnt_asc as signed)- cast(rnt_desc as signed))<=1
上面这种利用窗口函数的方法如果不用cast的方法会出现BIGINT UNSIGNED value is out of range in……的报错 并且还要注意两个窗口函数后面order by 的部分是有点点不同的
+-------+--------+-----------+--------+| empId | name | supervisor| salary |+-------+--------+-----------+--------+|1| John |3|1000||2| Dan |3|2000||3| Brad |null|4000||4| Thomas |3|4000|+-------+--------+-----------+--------+
student_id student_name gender dept_id
1 Jack M 12 Jane F 13 Mark M 2
department 表格:
dept_id dept_name
1 Engineering
2 Science
3 Law
示例输出为
dept_name student_number
Engineering 2
Science 1
Law 0
解题思路
考查点:
一个是表连接:使用左连接,可以获的没有学生的专业
一个是分组,对department.dept_name进行分组后求和
一个是排序,先对学生人数降序排序,在对department.dept_name升序排序
SELECT
department.dept_name
,COUNT(student_id) student_number
FROM department
LEFTJOIN student ON department.dept_id = student.dept_id
GROUPBY department.dept_name
ORDERBY student_number DESC,department.dept_name ASC
【每日SQL打卡】DAY 6丨寻找用户推荐人【难度简单】
给定表 customer ,里面保存了所有客户信息和他们的推荐人。
+------+------+-----------+| id | name | referee_id|+------+------+-----------+|1| Will |NULL||2| Jane |NULL||3| Alex |2||4| Bill |NULL||5| Zack |1||6| Mark |2|+------+------+-----------+
写一个查询语句,返回一个编号列表,列表中编号的推荐人的编号都 不是 2。
对于上面的示例数据,结果为:
+------+| name |+------+| Will || Jane || Bill || Zack |+------+
解题思路
此题同 DAY 5丨员工奖金【难度简单】 存在null值的判断
方法一:直接在判断里面使用referee_id IS NULL
SELECT name
FROM customer
WHERE referee_id !=2OR referee_id ISNULL
方法二:使用ifnull函数将为null的值转换成0后在判断
SELECT name
FROM customer
WHERE ifnull(referee_id,0)!=2
【每日SQL打卡】DAY 7丨大的国家【难度简单】
这里有张 World 表
+-----------------+------------+------------+--------------+---------------+| name | continent | area | population | gdp |+-----------------+------------+------------+--------------+---------------+| Afghanistan | Asia |652230|25500100|20343000|| Albania | Europe |28748|2831741|12960000|| Algeria | Africa |2381741|37100000|188681000|| Andorra | Europe |468|78115|3712000|| Angola | Africa |1246700|20609294|100990000|+-----------------+------------+------------+--------------+---------------+
如果一个国家的面积超过300万平方公里,或者人口超过2500万,那么这个国家就是大国家。
编写一个SQL查询,输出表中所有大国家的名称、人口和面积。
例如,根据上表,我们应该输出:
+--------------+-------------+--------------+| name | population | area |+--------------+-------------+--------------+| Afghanistan |25500100|652230|| Algeria |37100000|2381741|+--------------+-------------+--------------
解题思路
考查:where筛选
SELECT
name
,population
,area
FROM World
WHERE(area >3000000OR population >25000000)
SELECT
u1.id
,u1.time,u1.uid
,u1.is_suc
,u2.id res
,u2.timeFROM user_succ u1
LEFTJOIN(SELECT
id
,time,uid
FROM user_succ
WHERE is_suc =1) u2 ON u1.uid = u2.uid
AND u1.time> u2.time
这里利用窗口函数来找到我们需要的数据,这里注意窗口函数分组的依据是 u1.id和u1.uid
对u2.time进行降序排列,排在第一位的数据即为我们需要的数据
SELECT
t.id
,t.time,t.is_suc
,t.res
FROM(SELECT
u1.id
,u1.time,u1.uid
,u1.is_suc
,u2.id res
,u2.time 时间
,row_number()over(PARTITIONBY u1.id,u1.uid ORDERBY u2.timeDESC) r_k
FROM user_succ u1
LEFTJOIN(SELECT
id
,time,uid
FROM user_succ
WHERE is_suc =1) u2 ON u1.uid = u2.uid
AND u1.time> u2.time) t
where r_k =1
【每日SQL打卡】DAY 8丨判断三角形【难度简单】
一个小学生 Tim 的作业是判断三条线段是否能形成一个三角形。
然而,这个作业非常繁重,因为有几百组线段需要判断。
假设表 triangle 保存了所有三条线段的三元组 x, y, z ,你能帮 Tim 写一个查询语句,来判断每个三元组是否可以组成一个三角形吗?
x y z
131530102015
对于如上样例数据,你的查询语句应该返回如下结果:
x y z triangle
131530No102015 Yes
解题思路
利用case when函数进行判断
任意两边之差小于第三边:这里需要加上绝对值
任意两边之和大于第三边
为了更加严谨也可以先对x,y,z进行判断,是否为正数
SELECT
x
,y
,z
,casewhen((ABS(x - y)< z AND(x + y)> z)AND(ABS(x - z)< y AND(x + z)> y)AND(ABS(y - z)< x AND(y + z)> x))then"Yes"else"No"end triangle
FROM triangle
SELECT
s.pay_date
,e.department_id
,avg(s.amount) 每个部门的平均工资
FROM Salary s
LEFTJOIN employee e ON s.employee_id = e.employee_id
GROUPBY s.pay_date,e.department_id
再在上表的基础上,利用窗口函数求出每一个工资发放日,公司的平均工资
SELECT
substring(t1.pay_date,1,7) pay_month
,t1.department_id
,t1.每个部门的平均工资
,avg(t1.每个部门的平均工资)OVER(PARTITIONBY t1.pay_date) 公司的平均工资
FROM(SELECT
s.pay_date
,e.department_id
,avg(s.amount) 每个部门的平均工资
FROM Salary s
LEFTJOIN employee e ON s.employee_id = e.employee_id
GROUPBY s.pay_date,e.department_id
) t1
作为该电影院的信息部主管,您需要编写一个 SQL查询,找出所有影片描述为非 boring (不无聊) 的并且 id 为奇数 的影片,结果请按等级 rating 排列。
例如,下表 cinema:
+---------+-----------+--------------+-----------+| id | movie | description | rating |+---------+-----------+--------------+-----------+|1| War | great 3D |8.9||2| Science | fiction |8.5||3| irish | boring |6.2||4| Ice song | Fantacy |8.6||5| House card| Interesting|9.1|+---------+-----------+--------------+-----------+
对于上面的例子,则正确的输出是为:
+---------+-----------+--------------+-----------+| id | movie | description | rating |+---------+-----------+--------------+-----------+|5| House card| Interesting|9.1||1| War | great 3D |8.9|+---------+-----------+--------------+-----------+
解题思路
主要考查筛选以及排序
非 boring (不无聊)的影片:description != “boring”
id 为奇数:id除以2余数为1
rating 排列:按rating降序排列
SELECT
id
,movie
,description
,rating
FROM cinema
WHERE description !="boring"ANDMOD(id,2)=1ORDERBY rating DESC
SELECT
id
,student
,lag_s
,lead_s
,CASEWHEN id%2=1AND lead_s ISNOTNULLTHEN lead_s
WHEN id%2=0THEN lag_s
WHEN lead_s THEN student
END student
FROM(SELECT*,LAG(student)OVER()AS lag_s
,LEAD(student)OVER()AS lead_s
FROM seat
) t
【每日SQL打卡】DAY 10丨买下所有产品的客户【难度中等】
Customer 表: ±------------±--------+ | Column Name | Type | ±------------±--------+ | customer_id | int | | product_key | int | ±------------±--------+
product_key 是 Customer 表的外键。
Product 表: ±------------±--------+ | Column Name | Type | ±------------±--------+ | product_key | int | ±------------±--------+
SELECT
customer_id
FROM(SELECT
p.product_key
,c.customer_id
FROM Product p
LEFTJOIN customer c ON p.product_key = c.product_key
GROUPBY c.customer_id,p.product_key
) t
GROUPBY customer_id
havingcount(product_key)=(SELECTCOUNT(product_key)FROM Product)
SELECT
t.product_id
,t.year first_year
,t.quantity
,t.price
FROM(SELECT
product_id
,year,quantity
,price
,row_number()OVER(PARTITIONBY product_id ORDERBYyear) r_k
FROM sales
) t
WHERE r_k =1
当然不利用窗口函数同样可以解决问题
首先找出每个产品销售最小时间
在用原始的sales表格通过产品id以及时间连接上表后得到结果
SELECT
s.product_id
,s.year first_year
,s.quantity
,s.price
FROM sales s
JOIN(SELECT
product_id
,MIN(year) 最小时间
FROM sales
GROUPBY product_id
) t ON s.product_id = t.product_id AND s.year= t.最小时间
每一行是一个玩家的记录,他在某一天使用某个设备注销之前登录并玩了很多游戏(可能是 0) 我们将玩家的安装日期定义为该玩家的第一个登录日。 我们还将某个日期 X 的第 1 天留存时间定义为安装日期为 X 的玩家的数量,他们在 X 之后的一天重新登录,除以安装日期为 X 的玩家的数量,四舍五入到小数点后两位。
SELECT
product_id
,product_name
FROM(SELECT
p.product_id
,p.product_name
,CASEWHEN sale_date BETWEEN"2019-01-01"AND"2019-03-31"THEN"0"ELSE"1"END 是否只在2019年春季售出
FROM Product p
LEFTJOIN Sales s ON p.product_id = s.product_id
) t
GROUPBY product_id,product_name
HAVINGSUM(是否只在2019年春季售出)=0
【每日SQL打卡】DAY 13丨每日新用户统计【难度中等】
Traffic 表:
+---------------+---------+|Column Name |Type|+---------------+---------+| user_id |int|| activity |enum|| activity_date |date|+---------------+---------+
编写一个 SQL 查询来重新格式化表,使得新的表中有一个部门 id 列和一些对应 每个月 的收入(revenue)列。
查询结果格式如下面的示例所示:
Department 表:
+------+---------+-------+| id | revenue |month|+------+---------+-------+|1|8000| Jan ||2|9000| Jan ||3|10000| Feb ||1|7000| Feb ||1|6000| Mar |+------+---------+-------+
SELECT`id`,SUM(CASE`month`WHEN'Jan'THEN`revenue`ELSENULLEND)`Jan_Revenue`,SUM(CASE`month`WHEN'Feb'THEN`revenue`ELSENULLEND)`Feb_Revenue`,SUM(CASE`month`WHEN'Mar'THEN`revenue`ELSENULLEND)`Mar_Revenue`,SUM(CASE`month`WHEN'Apr'THEN`revenue`ELSENULLEND)`Apr_Revenue`,SUM(CASE`month`WHEN'May'THEN`revenue`ELSENULLEND)`May_Revenue`,SUM(CASE`month`WHEN'Jun'THEN`revenue`ELSENULLEND)`Jun_Revenue`,SUM(CASE`month`WHEN'Jul'THEN`revenue`ELSENULLEND)`Jul_Revenue`,SUM(CASE`month`WHEN'Aug'THEN`revenue`ELSENULLEND)`Aug_Revenue`,SUM(CASE`month`WHEN'Sep'THEN`revenue`ELSENULLEND)`Sep_Revenue`,SUM(CASE`month`WHEN'Oct'THEN`revenue`ELSENULLEND)`Oct_Revenue`,SUM(CASE`month`WHEN'Nov'THEN`revenue`ELSENULLEND)`Nov_Revenue`,SUM(CASE`month`WHEN'Dec'THEN`revenue`ELSENULLEND)`Dec_Revenue`FROM Department
GROUPBY`id`;
【每日SQL打卡】DAY 15丨查询活跃业务【难度中等】
事件表:Events
+---------------+---------+|Column Name |Type|+---------------+---------+| business_id |int|| event_type |varchar|| occurences |int|+---------------+---------+
SELECTDISTINCT spend_date,'mobile'AS platform
FROM spending
UNIONALLSELECTDISTINCT spend_date,'desktop'AS platform
FROM spending
UNIONALLSELECTDISTINCT spend_date,'both'AS platform
FROM spending
SELECT
A.spend_date
,A.platform
,IFNULL(B.total_amount,0) total_amount
,IFNULL(B.total_users,0) total_users
FROM(SELECTDISTINCT spend_date,'mobile'AS platform
FROM spending
UNIONALLSELECTDISTINCT spend_date,'desktop'AS platform
FROM spending
UNIONALLSELECTDISTINCT spend_date,'both'AS platform
FROM spending
) A
LEFTJOIN(SELECT
s.spend_date
,t2.new_platform
,SUM(amount) total_amount
,COUNT(DISTINCT s.user_id) total_users
FROM Spending s
LEFTJOIN(SELECT
user_id
,spend_date
,CASEWHEN ct =2THEN"both"WHEN ct =1AND platform ="desktop"THEN"desktop"WHEN ct =1AND platform ="mobile"THEN"mobile"END new_platform
FROM(SELECT
user_id
,spend_date
,platform
,COUNT(platform)over(PARTITIONBY spend_date,user_id) ct
FROM Spending
GROUPBY1,2,3) t1
groupby user_id,spend_date,new_platform
) t2 ON s.spend_date = t2.spend_date
AND s.user_id = t2.user_id
GROUPBY s.spend_date,t2.new_platform
) B ON A.spend_date = B.spend_date AND A.platform = B.new_platform
【每日SQL打卡】DAY 16丨报告的记录 II【难度中等】
动作表: Actions
+---------------+---------+|Column Name |Type|+---------------+---------+| user_id |int|| post_id |int|| action_date |date||action|enum|| extra |varchar|+---------------+---------+
SELECTdistinct post_id
,action_date
FROM Actions
WHERE extra ="spam"
在将上表直接连接Removals表
SELECTdistinct a.post_id
,a.action_date
,r.post_id
FROM Actions a
LEFTJOIN Removals r ON a.post_id = r.post_id
WHERE extra ="spam"
根据action_date计算每天的比例
SELECT
action_date
,COUNT(post_2)/COUNT(post_1) 比例
FROM(SELECTdistinct a.post_id post_1
,a.action_date
,r.post_id post_2
FROM Actions a
LEFTJOIN Removals r ON a.post_id = r.post_id
WHERE extra ="spam") t1
GROUPBY action_date
最后计算平均值
SELECTROUND(AVG(比例),2)*100 average_daily_percent
FROM(SELECT
action_date
,COUNT(post_2)/COUNT(post_1) 比例
FROM(SELECTdistinct a.post_id post_1
,a.action_date
,r.post_id post_2
FROM Actions a
LEFTJOIN Removals r ON a.post_id = r.post_id
WHERE extra ="spam") t1
GROUPBY action_date
) t2
【每日SQL打卡】DAY 16丨市场分析 II【难度困难】
表: Users
+----------------+---------+|Column Name |Type|+----------------+---------+| user_id |int|| join_date |date|| favorite_brand |varchar|+----------------+---------+
+------+---------+-------+| id | revenue |month|+------+---------+-------+|1|8000| Jan ||2|9000| Jan ||3|10000| Feb ||1|7000| Feb ||1|6000| Mar |+------+---------+-------+
SELECT`id`,SUM(CASE`month`WHEN'Jan'THEN`revenue`ELSENULLEND)`Jan_Revenue`,SUM(CASE`month`WHEN'Feb'THEN`revenue`ELSENULLEND)`Feb_Revenue`,SUM(CASE`month`WHEN'Mar'THEN`revenue`ELSENULLEND)`Mar_Revenue`,SUM(CASE`month`WHEN'Apr'THEN`revenue`ELSENULLEND)`Apr_Revenue`,SUM(CASE`month`WHEN'May'THEN`revenue`ELSENULLEND)`May_Revenue`,SUM(CASE`month`WHEN'Jun'THEN`revenue`ELSENULLEND)`Jun_Revenue`,SUM(CASE`month`WHEN'Jul'THEN`revenue`ELSENULLEND)`Jul_Revenue`,SUM(CASE`month`WHEN'Aug'THEN`revenue`ELSENULLEND)`Aug_Revenue`,SUM(CASE`month`WHEN'Sep'THEN`revenue`ELSENULLEND)`Sep_Revenue`,SUM(CASE`month`WHEN'Oct'THEN`revenue`ELSENULLEND)`Oct_Revenue`,SUM(CASE`month`WHEN'Nov'THEN`revenue`ELSENULLEND)`Nov_Revenue`,SUM(CASE`month`WHEN'Dec'THEN`revenue`ELSENULLEND)`Dec_Revenue`FROM Department
GROUPBY`id`;
+-----------+-------------------+--------+------+| person_id | person_name | weight | turn |+-----------+-------------------+--------+------+|5| George Washington |250|1||3| John Adams |350|2||6| Thomas Jefferson |400|3||2| Will Johnliams |200|4||4| Thomas Jefferson |175|5||1| James Elephant |500|6|+-----------+-------------------+--------+------+
Result 表
+-------------------+| person_name |+-------------------+| Thomas Jefferson |+-------------------+
为了简化,Queue 表按 turn 列由小到大排序。
上例中 George Washington(id 5), John Adams(id 3) 和 Thomas Jefferson(id 6) 将可以进入电梯,因为他们的体重和为 250 + 350 + 400 = 1000。
Thomas Jefferson(id 6) 是最后一个体重合适并进入电梯的人。
解题思路
此题思路主要是累计求和,按照turn排序对weight累计求和
找到累计求和小于1000且turn最大的那个人
SELECT
person_name
FROM Queue
WHERE turn =(SELECTMAX(turn)FROM(SELECT
person_id
,person_name
,weight
,turn
,SUM(weight)OVER(ORDERBY turn) 累计求和
FROM Queue
) t1
WHERE 累计求和 <=1000)
【每日SQL打卡】DAY 20丨查询结果的质量和占比【难度简单】
查询表 Queries
+-------------+---------+|Column Name |Type|+-------------+---------+| query_name |varchar|| result |varchar|| position |int|| rating |int|+-------------+---------+
+------------+-------------------+----------+--------+| query_name | result | position | rating |+------------+-------------------+----------+--------+| Dog | Golden Retriever |1|5|| Dog | German Shepherd |2|5|| Dog | Mule |200|1|| Cat | Shirazi |5|2|| Cat | Siamese |3|3|| Cat | Sphynx |7|4|+------------+-------------------+----------+--------+
+-----------+--------------+| team_id | team_name |+-----------+--------------+|10| Leetcode FC ||20| NewYork FC ||30| Atlanta FC ||40| Chicago FC ||50| Toronto FC |+-----------+--------------+
+------------+--------------+---------------+| team_id | team_name | num_points |+------------+--------------+---------------+|10| Leetcode FC |7||20| NewYork FC |3||50| Toronto FC |3||30| Atlanta FC |1||40| Chicago FC |0|+------------+--------------+---------------+
解题思路
首先利用CASE WHEN 计算每个team的分数
在用union all将host_team与guest_team的分数合并在一起
SELECT
host_team team
,CASEWHEN host_goals > guest_goals THEN3WHEN host_goals = guest_goals THEN1else0END score
FROM Matches
UNIONALLSELECT
guest_team team
,CASEWHEN host_goals < guest_goals THEN3WHEN host_goals = guest_goals THEN1else0END score
FROM Matches
输出结果
team score
------ --------103301103203503200101500300300
通过上表计算每一队的分数,使用Teams表进行左连接后发现team_id = 40的没有分数
在使用ifnull函数将null转换成0,最后进行排序
SELECT
Teams.team_id
,Teams.team_name
,ifnull(SUM(score),0) num_points
FROM Teams
LEFTJOIN(SELECT
host_team team
,CASEWHEN host_goals > guest_goals THEN3WHEN host_goals = guest_goals THEN1else0END score
FROM Matches
UNIONALLSELECT
guest_team team
,CASEWHEN host_goals < guest_goals THEN3WHEN host_goals = guest_goals THEN1else0END score
FROM Matches
) t ON Teams.team_id = t.team
GROUPBY Teams.team_id,Teams.team_name
ORDERBY num_points DESC,Teams.team_id
输出结果
team_id team_name num_points
------- ----------- ------------10 Leetcode FC 720 NewYork FC 350 Toronto FC 330 Atlanta FC 140 Chicago FC 0
+---------------+---------+|Column Name |Type|+---------------+---------+| product_id |int|| purchase_date |date|| units |int|+---------------+---------+
SELECT
u.product_id
,ROUND(SUM(u.units*p.price)/SUM(units),2) average_price
FROM UnitsSold u
LEFTJOIN Prices p
ON(u.product_id = p.product_id AND(u.purchase_date BETWEEN p.start_date AND p.end_date))GROUPBY u.product_id
【每日SQL打卡】DAY 22丨页面推荐【难度中等】
朋友关系列表: Friendship
+---------------+---------+|Column Name |Type|+---------------+---------+| user1_id |int|| user2_id |int|+---------------+---------+
这张表的主键是 (user1_id, user2_id)。
这张表的每一行代表着 user1_id 和 user2_id 之间存在着朋友关系。
喜欢列表: Likes
+-------------+---------+|Column Name |Type|+-------------+---------+| user_id |int|| page_id |int|+-------------+---------+
SELECTDISTINCT page_id recommended_page
FROM(SELECT
f.user1_id
,f.user2_id
,CASEWHEN f.user2_id =1THEN f.user1_id ELSE f.user2_id END 新列
FROM Friendship f
WHERE(f.user1_id =1OR f.user2_id =1)) t
LEFTJOIN Likes l ON t.新列 = l.user_id
WHERE l.page_id NOTIN(SELECTDISTINCT l.page_id
FROM Friendship f
LEFTJOIN Likes l ON f.user1_id = l.user_id
WHERE f.user1_id =1)ORDERBY page_id
通过这题有点懂了目前大数据推荐,比如某短视频平台不断给你推荐你感兴趣的东西
【每日SQL打卡】DAY 23丨向CEO汇报工作的人【难度中等】
员工表:Employees
+---------------+---------+|Column Name |Type|+---------------+---------+| employee_id |int|| employee_name |varchar|| manager_id |int|+---------------+---------+
+-------------+---------------+------------+| employee_id | employee_name | manager_id |+-------------+---------------+------------+|1| Boss |1||3| Alice |3||2| Bob |1||4| Daniel |2||7| Luis |4||8| Jhon |3||9| Angela |8||77| Robert |1|+-------------+---------------+------------+
SELECT
e1.employee_id
FROM Employees e1
LEFTJOIN Employees e2 ON e1.manager_id = e2.employee_id
LEFTJOIN Employees e3 ON e2.manager_id = e3.employee_id
WHERE e3.manager_id =1AND e1.employee_id !=1
【每日SQL打卡】DAY 23丨学生们参加各科测试的次数【难度简单】
学生表: Students
+---------------+---------+|Column Name |Type|+---------------+---------+| student_id |int|| student_name |varchar|+---------------+---------+
主键为 student_id(学生ID),该表内的每一行都记录有学校一名学生的信息。
科目表: Subjects
+--------------+---------+|Column Name |Type|+--------------+---------+| subject_name |varchar|+--------------+---------+
主键为 subject_name(科目名称),每一行记录学校的一门科目名称。
考试表: Examinations
+--------------+---------+|Column Name |Type|+--------------+---------+| student_id |int|| subject_name |varchar|+--------------+---------+
这张表压根没有主键,可能会有重复行。
学生表里的一个学生修读科目表里的每一门科目,
而这张考试表的每一行记录就表示学生表里的某个学生参加了一次科目表里某门科目的测试。
解题思路
SELECT a.student_id, a.student_name, b.subject_name,COUNT(e.subject_name)AS attended_exams
FROM Students a CROSSJOIN Subjects b
LEFTJOIN Examinations e ON a.student_id = e.student_id AND b.subject_name = e.subject_name
GROUPBY a.student_id, b.subject_name
ORDERBY a.student_id, b.subject_name
CROSS JOIN Mysql中没有full outer join hive中可以用
【每日SQL打卡】DAY 24丨不同国家的天气类型【难度简单】
国家表:Countries
+---------------+---------+|Column Name |Type|+---------------+---------+| country_id |int|| country_name |varchar|+---------------+---------+
country_id 是这张表的主键。
该表的每行有 country_id 和 country_name 两列。
天气表:Weather
+---------------+---------+|Column Name |Type|+---------------+---------+| country_id |int|| weather_state |varchar||day|date|+---------------+---------+
+------------+--------------+| country_id | country_name |+------------+--------------+|2| USA ||3| Australia ||7| Peru ||5| China ||8| Morocco ||9| Spain |+------------+--------------+
+--------------+--------------+| country_name | weather_type |+--------------+--------------+| USA | Cold || Austraila | Cold || Peru | Hot || China | Warm || Morocco | Hot |+--------------+--------------+
SELECT
c.country_name
,casewhenround(AVG(weather_state),2)<=15then"Cold"whenround(AVG(weather_state),2)>=25then"Hot"else"Warm"end weather_type
FROM Weather w
LEFTJOIN Countries c ON w.country_id = c.country_id
WHERE w.dayBETWEEN"2019-11-01"AND"2019-11-30"GROUPBY w.country_id
【每日SQL打卡】DAY 24丨找到连续区间的开始和结束数字【难度中等】
表:Logs
+---------------+---------+|Column Name |Type|+---------------+---------+| log_id |int|+---------------+---------+
id 是上表的主键。
上表的每一行包含日志表中的一个 ID。
后来一些 ID 从 Logs 表中删除。编写一个 SQL 查询得到 Logs 表中的连续区间的开始数字和结束数字。
+-------------+--------+------------+--------------+| player_name | gender |day| score_points |+-------------+--------+------------+--------------+| Aron | F |2020-01-01|17|| Alice | F |2020-01-07|23|| Bajrang | M |2020-01-07|7|| Khali | M |2019-12-25|11|| Slaman | M |2019-12-30|13|| Joe | M |2019-12-31|3|| Jose | M |2019-12-18|2|| Priya | F |2019-12-31|23|| Priyanka | F |2019-12-30|17|+-------------+--------+------------+--------------+
结果表:
+--------+------------+-------+| gender |day| total |+--------+------------+-------+| F |2019-12-30|17|| F |2019-12-31|40|| F |2020-01-01|57|| F |2020-01-07|80|| M |2019-12-18|2|| M |2019-12-25|13|| M |2019-12-30|26|| M |2019-12-31|29|| M |2020-01-07|36|+--------+------------+-------+
女性队伍:
第一天是 2019-12-30,Priyanka 获得 17 分,队伍的总分是 17 分
第二天是 2019-12-31, Priya 获得 23 分,队伍的总分是 40 分
第三天是 2020-01-01, Aron 获得 17 分,队伍的总分是 57 分
第四天是 2020-01-07, Alice 获得 23 分,队伍的总分是 80 分
男性队伍:
第一天是 2019-12-18, Jose 获得 2 分,队伍的总分是 2 分
第二天是 2019-12-25, Khali 获得 11 分,队伍的总分是 13 分
第三天是 2019-12-30, Slaman 获得 13 分,队伍的总分是 26 分
第四天是 2019-12-31, Joe 获得 3 分,队伍的总分是 29 分
第五天是 2020-01-07, Bajrang 获得 7 分,队伍的总分是 36 分
解题思路
利用窗口函数累计求和
SELECT
gender
,DAY,SUM(score_points)OVER(PARTITIONBY gender ORDERBYDAY) total
FROM Scores
ORDERBY gender,DAY
【每日SQL打卡】DAY 26丨广告效果【难度简单】
表: Ads
+---------------+---------+|Column Name |Type|+---------------+---------+| ad_id |int|| user_id |int||action|enum|+---------------+---------+
SELECT
p.product_name
,SUM(unit) unit
FROM Products p
LEFTJOIN Orders o ON p.product_id = o.product_id
WHERE SUBSTRING(o.order_date,1,7)="2020-02"GROUPBY p.product_name
HAVINGSUM(unit)>=100
SELECT
s1.SId 学生学号
,Student.`Sname` 学生姓名
,C1.`Cname`01课程
,s1.score 01课程成绩
,C2.`Cname`02课程
,s2.score 02课程成绩
FROM SC s1
JOIN SC s2 ON(s1.SId = s2.SId AND s1.CId ="01"AND s2.CId ="02"AND s1.score > s2.score)LEFTJOIN Student ON s1.SId = Student.SId
LEFTJOIN Course C1 ON s1.CId = C1.`CId`LEFTJOIN Course C2 ON s2.CId = C2.`CId`
输出
学生学号
学生姓名
01课程
01课程成绩
02课程
02课程成绩
02
钱电
语文
70.0
数学
60.0
04
李云
语文
50.0
数学
30.0
2.查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩
SELECT
SC.SId 学生学号
,Student.`Sname` 学生姓名
,ROUND(AVG(SC.score),2) 平均成绩
FROM SC
LEFTJOIN Student ON SC.SId = Student.SId
GROUPBY SC.SId
HAVINGAVG(SC.score)>=60
输出
学生学号
学生姓名
平均成绩
08
(NULL)
94.50
01
西红柿
89.67
02
钱电
70.00
03
孙风
80.00
05
周梅
81.50
07
郑竹
93.50
3.查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为 NULL )
SELECT
Student.SId 学生学号
,Student.`Sname` 学生姓名
,COUNT(SC.`CId`) 选课总数
,SUM(SC.`score`) 总成绩
FROM Student
LEFTJOIN SC ON Student.SId = SC.SId
GROUPBY Student.SId,Student.`Sname`
输出
学生学号
学生姓名
选课总数
总成绩
01
西红柿
3
269.0
02
钱电
3
210.0
03
孙风
3
240.0
04
李云
3
100.0
05
周梅
2
163.0
06
吴兰
2
65.0
07
郑竹
2
187.0
09
张三
0
(NULL)
10
西红柿
0
(NULL)
11
李四
0
(NULL)
12
赵六
0
(NULL)
13
孙七
0
(NULL)
4.查询学过「张三」老师授课的同学的信息
SELECT
S.SId 学生学号
,S.`Sname` 学生姓名
FROM Teacher T
LEFTJOIN Course C ON T.TId = C.TId
LEFTJOIN SC ON SC.CId = C.CId
LEFTJOIN Student S ON S.SId = SC.SId
WHERE T.`Tname`="张三"
输出
学生学号
学生姓名
07
郑竹
05
周梅
04
李云
03
孙风
02
钱电
01
西红柿
5.查询没有学全所有课程的同学的信息
将同学的选课总数与总课程数对比,找出没有学所有课程的同学
SELECT
Student.SId 学生学号
,Student.`Sname` 学生姓名
FROM Student
LEFTJOIN SC ON Student.SId = SC.SId
GROUPBY Student.SId,Student.`Sname`HAVINGCOUNT(SC.`CId`)<(SELECTCOUNT(CId)FROM Course)
输出
学生学号
学生姓名
05
周梅
06
吴兰
07
郑竹
09
张三
10
西红柿
11
李四
12
赵六
13
孙七
6.查询和" 01 "号的同学学习的课程 完全相同的其他同学的信息
SELECT
学生姓名
FROM(SELECT
Student.SId 学生学号
,Student.`Sname` 学生姓名
,SC.`CId` 课程编号
FROM Student
LEFTJOIN SC ON Student.SId = SC.SId
WHERE SC.`CId`IN(SELECT
SC.`CId` 课程编号
FROM Student
LEFTJOIN SC ON Student.SId = SC.SId
WHERE Student.SId ="01")AND Student.SId !="01") t1
GROUPBY 学生姓名
HAVINGCOUNT(课程编号)=(SELECTCOUNT(SC.`CId`)FROM Student
LEFTJOIN SC ON Student.SId = SC.SId
WHERE Student.SId ="01")
输出
学生姓名
钱电
孙风
李云
7.查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩
SELECT
Student.SId 学生学号
,Student.`Sname` 学生姓名
,ROUND(AVG(SC.`score`),2) 平均成绩
FROM Student
LEFTJOIN SC ON Student.SId = SC.SId
WHERE Student.SId IN(SELECT
学生学号
FROM(SELECT
Student.SId 学生学号
,Student.`Sname` 学生姓名
,SC.`CId` 课程编号
,SC.`score` 成绩
FROM Student
LEFTJOIN SC ON Student.SId = SC.SId
WHERE SC.`score`<60) t1
GROUPBY 学生学号,学生姓名
HAVINGCOUNT(课程编号)>=2)GROUPBY Student.SId,Student.`Sname`
输出
学生学号
学生姓名
平均成绩
04
李云
33.33
06
吴兰
32.50
8.检索" 01 "课程分数小于 60,按分数降序排列的学生信息
SELECT
Student.SId 学生学号
,Student.`Sname` 学生姓名
FROM SC
LEFTJOIN Student ON SC.`SId`= Student.`SId`WHERE SC.CId ="01"AND SC.score <60ORDERBY Student.SId DESC
输出
学生编号
学生姓名
06
吴兰
04
李云
9.按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩
SELECT
学生学号
,学生姓名
,课程名
,成绩
,平均成绩
FROM(SELECT
Student.SId 学生学号
,Student.`Sname` 学生姓名
,Course.`Cname` 课程名
,SC.`score` 成绩
,ROUND(AVG(SC.`score`)over(PARTITIONBY Student.SId),2) 平均成绩
FROM Student
LEFTJOIN SC ON Student.SId = SC.SId
LEFTJOIN Course ON SC.CId = Course.CId
WHERE SC.`score`ISNOTNULL) t1
ORDERBY 平均成绩 DESC
SELECT
S.SId 学生学号
,S.`Sname` 学生姓名
,SC.score 成绩
FROM Teacher T
LEFTJOIN Course C ON T.TId = C.TId
LEFTJOIN SC ON SC.CId = C.CId
LEFTJOIN Student S ON S.SId = SC.SId
WHERE T.`Tname`="张三"AND SC.score =(SELECTMAX(SC.score)FROM Teacher T
LEFTJOIN Course C ON T.TId = C.TId
LEFTJOIN SC ON SC.CId = C.CId
WHERE T.`Tname`="张三")
输出
学生学号
学生姓名
成绩
01
西红柿
90.0
26.查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩
SELECT
sc1.CId 课程编号
,sc1.SId 学生编号
,sc1.score 学生成绩
FROM SC sc1,SC sc2
WHERE sc1.score = sc2.score
AND sc1.SId = sc2.SId
AND sc1.CId != sc2.CId
GROUPBY sc1.CId,sc1.SId,sc1.score
输出
课程编号
学生编号
学生成绩
03
03
80.0
02
03
80.0
01
03
80.0
27.查询每门功成绩最好的前两名
SELECT
课程名
,学生学号
FROM(SELECT
Course.`Cname` 课程名
,SC.SId 学生学号
,rank()over(PARTITIONBY SC.CId ORDERBY SC.score DESC) 排名
FROM Course
LEFTJOIN SC ON Course.CId = SC.CId
) t
WHERE 排名 IN(1,2)
统计每门课程的学生选修人数(超过 5 人的课程才统计)。
SELECT
Course.`Cname` 课程名
,COUNT(SC.SId) 选修人数
FROM Course
LEFTJOIN SC ON Course.CId = SC.CId
GROUPBY Course.CId
HAVINGCOUNT(SC.SId)>5
输出
课程名
选修人数
语文
6
数学
6
英语
8
28.检索至少选修两门课程的学生学号
SELECT
SC.SId 学生学号
FROM SC
GROUPBY SC.SId
HAVINGCOUNT(SC.CId)>=2
输出
学生学号
08
01
02
03
04
05
06
07
29.查询选修了全部课程的学生信息
SELECT
Student.SId 学生编号
,Student.`Sname` 学生姓名
FROM Student
LEFTJOIN SC ON Student.SId = SC.SId
GROUPBY Student.SId,Student.`Sname`HAVINGCOUNT(SC.`CId`)=(SELECTCOUNT(CId)FROM Course)
输出
学生编号
学生姓名
01
西红柿
02
钱电
03
孙风
04
李云
30.查询各学生的年龄,只按年份来算
SELECT
Student.SId 学生学号
,Student.`Sname` 学生姓名
,Student.Sage 学生年份
FROM Student