vlambda博客
学习文章列表

力扣(LeetCode)数据库SQL最新108题(8)


以下题目均来自力扣(LeetCode)官网和其他网站,仅用作数据库爱好者学习交流,严禁进行商业及任何非法用途。


1280. 学生们参加各科测试的次数

学生表: 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 |
+--------------+---------+
这张表压根没有主键,可能会有重复行。
学生表里的一个学生修读科目表里的每一门科目,而这张考试表的每一行记录就表示学生表里的某个学生参加了一次科目表里某门科目的测试。

要求写一段 SQL 语句,查询出每个学生参加每一门科目测试的次数,结果按 student_id 和 subject_name 排序。

查询结构格式如下所示:

Students table:
+------------+--------------+
| student_id | student_name |
+------------+--------------+
| 1 | Alice |
| 2 | Bob |
| 13 | John |
| 6 | Alex |
+------------+--------------+
Subjects table:
+--------------+
| subject_name |
+--------------+
| Math |
| Physics |
| Programming |
+--------------+
Examinations table:
+------------+--------------+
| student_id | subject_name |
+------------+--------------+
| 1 | Math |
| 1 | Physics |
| 1 | Programming |
| 2 | Programming |
| 1 | Physics |
| 1 | Math |
| 13 | Math |
| 13 | Programming |
| 13 | Physics |
| 2 | Math |
| 1 | Math |
+------------+--------------+
Result table:
+------------+--------------+--------------+----------------+
| student_id | student_name | subject_name | attended_exams |
+------------+--------------+--------------+----------------+
| 1 | Alice | Math | 3 |
| 1 | Alice | Physics | 2 |
| 1 | Alice | Programming | 1 |
| 2 | Bob | Math | 1 |
| 2 | Bob | Physics | 0 |
| 2 | Bob | Programming | 1 |
| 6 | Alex | Math | 0 |
| 6 | Alex | Physics | 0 |
| 6 | Alex | Programming | 0 |
| 13 | John | Math | 1 |
| 13 | John | Physics | 1 |
| 13 | John | Programming | 1 |
+------------+--------------+--------------+----------------+

结果表需包含所有学生和所有科目(即便测试次数为0):
Alice 参加了 3 次数学测试, 2 次物理测试,以及 1 次编程测试;
Bob 参加了 1 次数学测试, 1 次编程测试,没有参加物理测试;
Alex 啥测试都没参加;
John 参加了数学、物理、编程测试各 1 次。
SELECT a.student_id, a.student_name, b.subject_name, COUNT(e.subject_name) AS attended_examsFROM Students a CROSS JOIN Subjects b LEFT JOIN Examinations e ON a.student_id = e.student_id AND b.subject_name = e.subject_nameGROUP BY a.student_id, a.student_name,b.subject_nameORDER BY a.student_id, b.subject_name;

这里不能用COUNT(*),要用COUNT(e.subject_name),因为e.subject_name中才有NULL


1285. 找到连续区间的开始和结束数字

表:Logs

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| log_id | int |
+---------------+---------+
id 是上表的主键。
上表的每一行包含日志表中的一个 ID。

后来一些 ID 从 Logs 表中删除。编写一个 SQL 查询得到 Logs 表中的连续区间的开始数字和结束数字。

将查询表按照 start_id 排序。

查询结果格式如下面的例子:

Logs 表:
+------------+
| log_id |
+------------+
| 1 |
| 2 |
| 3 |
| 7 |
| 8 |
| 10 |
+------------+

结果表:
+------------+--------------+
| start_id | end_id |
+------------+--------------+
| 1 | 3 |
| 7 | 8 |
| 10 | 10 |
+------------+--------------+
结果表应包含 Logs 表中的所有区间。
从 1 到 3 在表中。
从 4 到 6 不在表中。
从 7 到 8 在表中。
9 不在表中。
10 在表中。

由于并不懂@这种方法,在力扣上有朋友用非常巧妙的方法解出来了,故直接引用来了,谢谢这位朋友(马师傅eric)。


思路:

从结果样例来看,分别需要输出连续数列的头和尾。

那么,我们需要加工并获取一个连续数列的头,以及其对应的数列(如1的123,后续用max来取尾巴)或者直接是数列的尾巴。另外还要面对10这种单个数字存在。

这里分别获取头和尾比较简单:

数列的头的获取:

select log_id from logs where log_id-1 not in (select * from logs)

可以得到1,7,10. 因为连续数列中 前一个不存在的话,就是头了(也包括了单个数字的情况)

数列的尾的获取:

select log_id from logs where log_id+1 not in (select * from logs)

可以得到3,8,10. 因为连续数列中 后一个不存在的话,就是尾了(也包括了单个数字的情况)

自连接+where获取结果:

如果让前两行数据进行笛卡尔积,并且限制尾的数字必须不小于头的数字的话,就可以轻松得出结果。

完整代码:

select a.log_id as START_ID ,min(b.log_id) as END_ID from (select log_id from logs where log_id-1 not in (select * from logs)) a,(select log_id from logs where log_id+1 not in (select * from logs)) bwhere b.log_id>=a.log_idgroup by a.log_id;

还发现一种巧妙的方法(贾里Frank),简直厉害:

SELECT MIN(log_id)start_id, MAX(log_id) end_idFROM  (SELECT log_id, ROW_NUMBER() OVER(ORDER BY log_id) -log_id AS Grp FROM Logs)GROUP BY GrpORDER BY MIN(log_id);

受启发于本题方法一,在中的1225题,可以作如下查询,经验证可通过:

select a.period_state "period_state",to_char(a.dat,'yyyy-MM-dd') "start_date",min(to_char(b.dat,'yyyy-MM-dd')) "end_date" from (select 'succeeded' period_state,success_date dat from succeeded where success_date-1 not in (select * from succeeded  where success_date between to_date('2019-01-01','yyyy-mm-dd') and to_date('2019-12-31','yyyy-mm-dd'))  and success_date between to_date('2019-01-01','yyyy-mm-dd') and to_date('2019-12-31','yyyy-mm-dd')) a, (select 'succeeded' period_state,success_date dat from succeeded where success_date+1 not in (select * from succeeded  where success_date between to_date('2019-01-01','yyyy-mm-dd') and to_date('2019-12-31','yyyy-mm-dd'))  and success_date between to_date('2019-01-01','yyyy-mm-dd') and to_date('2019-12-31','yyyy-mm-dd')) bwhere b.dat>=a.datgroup by a.period_state,a.datunion allselect a.period_state "period_state",to_char(a.dat,'yyyy-MM-dd') "start_date",min(to_char(b.dat,'yyyy-MM-dd')) "end_date" from (select 'failed' period_state,fail_date dat from failed where fail_date-1 not in (select * from failed  where fail_date between to_date('2019-01-01','yyyy-mm-dd') and to_date('2019-12-31','yyyy-mm-dd'))  and fail_date between to_date('2019-01-01','yyyy-mm-dd') and to_date('2019-12-31','yyyy-mm-dd')) a, (select 'failed' period_state,fail_date dat from failed where fail_date+1 not in (select * from failed  where fail_date between to_date('2019-01-01','yyyy-mm-dd') and to_date('2019-12-31','yyyy-mm-dd'))  and fail_date between to_date('2019-01-01','yyyy-mm-dd') and to_date('2019-12-31','yyyy-mm-dd')) bwhere b.dat>=a.datgroup by a.period_state,a.datorder by 2;

但是方法二好像总是不能得到想要的答案,尝试寻求其他更好的解法。


1294. 不同国家的天气类型

国家表: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, day) 是该表的复合主键。
该表的每一行记录了某个国家某一天的天气情况。

写一段 SQL 来找到表中每个国家在 2019 年 11 月的天气类型。

天气类型的定义如下:当 weather_state 的平均值小于或等于15返回 Cold,当 weather_state 的平均值大于或等于 25 返回 Hot,否则返回 Warm

你可以以任意顺序返回你的查询结果。

查询结果格式如下所示:

Countries table:
+------------+--------------+
| country_id | country_name |
+------------+--------------+
| 2 | USA |
| 3 | Australia |
| 7 | Peru |
| 5 | China |
| 8 | Morocco |
| 9 | Spain |
+------------+--------------+
Weather table:
+------------+---------------+------------+
| country_id | weather_state | day |
+------------+---------------+------------+
| 2 | 15 | 2019-11-01 |
| 2 | 12 | 2019-10-28 |
| 2 | 12 | 2019-10-27 |
| 3 | -2 | 2019-11-10 |
| 3 | 0 | 2019-11-11 |
| 3 | 3 | 2019-11-12 |
| 5 | 16 | 2019-11-07 |
| 5 | 18 | 2019-11-09 |
| 5 | 21 | 2019-11-23 |
| 7 | 25 | 2019-11-28 |
| 7 | 22 | 2019-12-01 |
| 7 | 20 | 2019-12-02 |
| 8 | 25 | 2019-11-05 |
| 8 | 27 | 2019-11-15 |
| 8 | 31 | 2019-11-25 |
| 9 | 7 | 2019-10-23 |
| 9 | 3 | 2019-12-23 |
+------------+---------------+------------+
Result table:
+--------------+--------------+
| country_name | weather_type |
+--------------+--------------+
| USA | Cold |
| Austraila | Cold |
| Peru | Hot |
| China | Warm |
| Morocco | Hot |
+--------------+--------------+
USA 11 月的平均 weather_state 为 (15) / 1 = 15 所以天气类型为 Cold。
Australia 11 月的平均 weather_state 为 (-2 + 0 + 3) / 3 = 0.333 所以天气类型为 Cold。
Peru 11 月的平均 weather_state 为 (25) / 1 = 25 所以天气类型为 Hot。
China 11 月的平均 weather_state 为 (16 + 18 + 21) / 3 = 18.333 所以天气类型为 Warm。
Morocco 11 月的平均 weather_state 为 (25 + 27 + 31) / 3 = 27.667 所以天气类型为 Hot。
我们并不知道 Spain 在 11 月的 weather_state 情况所以无需将他包含在结果中。
select c.country_name, (case when avg(w.weather_state)<=15 then 'Cold' when avg(w.weather_state)>=25 then 'Hot' else 'Warm' end ) weather_type from weather wleft join countries con c.country_id=w.country_idwhere day between '2019-11-01' and '2019-11-30'group by c.country_name;

或者:

select c.country_name,(case when avg(w.weather_state)<=15 then 'Cold'when avg(w.weather_state)>=25 then 'Hot'else 'Warm' end) weather_type from weather wleft join countries con c.country_id=w.country_idwhere day between '2019-11-01' and '2019-11-30'group by w.country_id;


1303. 求团队人数

员工表:Employee

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| employee_id | int |
| team_id | int |
+---------------+---------+
employee_id 字段是这张表的主键,表中的每一行都包含每个员工的 ID 和他们所属的团队。

编写一个 SQL 查询,以求得每个员工所在团队的总人数。

查询结果中的顺序无特定要求。

查询结果格式示例如下:

 
   
   
 
Employee Table:
+-------------+------------+
| employee_id | team_id |
+-------------+------------+
| 1 | 8 |
| 2 | 8 |
| 3 | 8 |
| 4 | 7 |
| 5 | 9 |
| 6 | 9 |
+-------------+------------+
Result table:
+-------------+------------+
| employee_id | team_size |
+-------------+------------+
| 1 | 3 |
| 2 | 3 |
| 3 | 3 |
| 4 | 1 |
| 5 | 2 |
| 6 | 2 |
+-------------+------------+
ID 为 1、2、3 的员工是 team_id 为 8 的团队的成员,
ID 为 4 的员工是 team_id 为 7 的团队的成员,
ID 为 5、6 的员工是 team_id 为 9 的团队的成员。
select employee_id,count(employee_id) over(partition by team_id) "team_size"from employeeorder by employee_id;

或者:

select employee_id,cn "team_size"from employee e,(select team_id,count(employee_id) cn from employee group by team_id) awhere e.team_id=a.team_idorder by employee_id;


1308. 不同性别每日分数总计

表: Scores

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| player_name | varchar |
| gender | varchar |
| day | date |
| score_points | int |
+---------------+---------+
(gender, day)是该表的主键
一场比赛是在女队和男队之间举行的
该表的每一行表示一个名叫 (player_name) 性别为 (gender) 的参赛者在某一天获得了 (score_points) 的分数
如果参赛者是女性,那么 gender 列为 'F',如果参赛者是男性,那么 gender 列为 'M'

写一条SQL语句查询每种性别在每一天的总分,并按性别和日期对查询结果排序

下面是查询结果格式的例子:

Scores表:
+-------------+--------+------------+--------------+
| 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 s1.gender, s1.day, SUM(s2.score_points) AS totalFROM Scores AS s1, Scores AS s2WHERE s1.gender = s2.gender AND s1.day >= s2.dayGROUP BY s1.gender, s1.dayORDER BY s1.gender, s1.day;

求累计和,或者用窗口函数:

select gender,to_char(day,'yyyy-mm-dd') day,sum(score_points) over(partition by gender order by day) total from scores;


1321. 餐馆营业额变化增长

表: Customer

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| customer_id | int |
| name | varchar |
| visited_on | date |
| amount | int |
+---------------+---------+
(customer_id, visited_on) 是该表的主键
该表包含一家餐馆的顾客交易数据
visited_on 表示 (customer_id) 的顾客在 visited_on 那天访问了餐馆
amount 是一个顾客某一天的消费总额

你是餐馆的老板,现在你想分析一下可能的营业额变化增长(每天至少有一位顾客)

写一条 SQL 查询计算以 7 天(某日期 + 该日期前的 6 天)为一个时间段的顾客消费平均值

查询结果格式的例子如下:

  • 查询结果按 visited_on 排序

  • average_amount 要 保留两位小数,日期数据的格式为 ('YYYY-MM-DD')

Customer 表:

+-------------+--------------+--------------+-------------+
| customer_id | name | visited_on | amount |
+-------------+--------------+--------------+-------------+
| 1 | Jhon | 2019-01-01 | 100 |
| 2 | Daniel | 2019-01-02 | 110 |
| 3 | Jade | 2019-01-03 | 120 |
| 4 | Khaled | 2019-01-04 | 130 |
| 5 | Winston | 2019-01-05 | 110 |
| 6 | Elvis | 2019-01-06 | 140 |
| 7 | Anna | 2019-01-07 | 150 |
| 8 | Maria | 2019-01-08 | 80 |
| 9 | Jaze | 2019-01-09 | 110 |
| 1 | Jhon | 2019-01-10 | 130 |
| 3 | Jade | 2019-01-10 | 150 |
+-------------+--------------+--------------+-------------+

结果表:
+--------------+--------------+----------------+
| visited_on | amount | average_amount |
+--------------+--------------+----------------+
| 2019-01-07 | 860 | 122.86 |
| 2019-01-08 | 840 | 120 |
| 2019-01-09 | 840 | 120 |
| 2019-01-10 | 1000 | 142.86 |
+--------------+--------------+----------------+
第一个七天消费平均值从 2019-01-01 到 2019-01-07 是 (100 + 110 + 120 + 130 + 110 + 140 + 150)/7 = 122.86
第二个七天消费平均值从 2019-01-02 到 2019-01-08 是 (110 + 120 + 130 + 110 + 140 + 150 + 80)/7 = 120
第三个七天消费平均值从 2019-01-03 到 2019-01-09 是 (120 + 130 + 110 + 140 + 150 + 80 + 110)/7 = 120
第四个七天消费平均值从 2019-01-04 到 2019-01-10 是 (130 + 110 + 140 + 150 + 80 + 110 + 130 + 150)/7 = 142.86
select to_char(visited_on,'yyyy-MM-dd') visited_on, amount, round(amount / 7, 2) average_amountfrom (select visited_on, sum(amount) over(order by visited_on rows between 6 preceding and current row) amount from (select visited_on, sum(amount) amount from Customer group by visited_on))where visited_on in (select distinct visited_on from Customer where to_number(to_char(visited_on, 'yyyyMMdd')) - 6 >= (select to_number(to_char(min(visited_on), 'yyyyMMdd'))  from Customer) );

或者:

SELECT a.visited_on, sum( b.amount ) AS amount, round(sum( b.amount ) / 7, 2 ) AS average_amount FROM ( SELECT DISTINCT visited_on FROM customer ) a JOIN customer b  ON datediff( a.visited_on, b.visited_on ) BETWEEN 0 AND 6 WHERE a.visited_on >= (SELECT min( visited_on ) FROM customer ) + 6 GROUP BY a.visited_on;

在oracle中用to_number( a.visited_on- b.visited_on )替换

datediff( a.visited_on, b.visited_on ):

SELECT to_char(a.visited_on,'yyyy-mm-dd') visited_on, sum( b.amount ) AS amount, round(sum( b.amount ) / 7, 2 ) AS average_amount FROM ( SELECT DISTINCT visited_on FROM customer ) a JOIN customer b  ON to_number( a.visited_on- b.visited_on ) BETWEEN 0 AND 6 WHERE a.visited_on >= (SELECT min( visited_on ) FROM customer ) + 6 GROUP BY a.visited_onorder by visited_on;


1322. 广告效果

表: Ads

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| ad_id | int |
| user_id | int |
| action | enum |
+---------------+---------+
(ad_id, user_id) 是该表的主键
该表的每一行包含一条广告的 ID(ad_id),用户的 ID(user_id) 和用户对广告采取的行为 (action)
action 列是一个枚举类型 ('Clicked', 'Viewed', 'Ignored') 。

一家公司正在运营这些广告并想计算每条广告的效果。

广告效果用点击通过率(Click-Through Rate:CTR)来衡量,公式如下:

写一条SQL语句来查询每一条广告的 ctr ,

 ctr 要保留两位小数。结果需要按 ctr 降序、按 ad_id 升序 进行排序。

查询结果示例如下:

Ads 表:
+-------+---------+---------+
| ad_id | user_id | action |
+-------+---------+---------+
| 1 | 1 | Clicked |
| 2 | 2 | Clicked |
| 3 | 3 | Viewed |
| 5 | 5 | Ignored |
| 1 | 7 | Ignored |
| 2 | 7 | Viewed |
| 3 | 5 | Clicked |
| 1 | 4 | Viewed |
| 2 | 11 | Viewed |
| 1 | 2 | Clicked |
+-------+---------+---------+
结果表:
+-------+-------+
| ad_id | ctr |
+-------+-------+
| 1 | 66.67 |
| 3 | 50.00 |
| 2 | 33.33 |
| 5 | 0.00 |
+-------+-------+
对于 ad_id = 1, ctr = (2/(2+1)) * 100 = 66.67
对于 ad_id = 2, ctr = (1/(1+2)) * 100 = 33.33
对于 ad_id = 3, ctr = (1/(1+1)) * 100 = 50.00
对于 ad_id = 5, ctr = 0.00, 注意 ad_id = 5 没有被点击 (Clicked) 或查看 (Viewed) 过
注意我们不关心 action 为 Ingnored 的广告
结果按 ctr(降序),ad_id(升序)排序
select ad_id,round((case when c+v=0 then 0 else c/(c+v) end)*100,2) ctrfrom ( select ad_id,count(case when action='Clicked' then 1 else null end) c,count(case when action='Viewed' then 1 else null end) v from ads group by ad_id) aorder by ctr desc,ad_id;

或者(对于没有点击的广告,会出现null,这时候用ifnull来置0,以此解决。oracle中用nvl解决):

SELECT ad_id,IFNULL(ROUND(SUM(action = 'Clicked')/(SUM(action = 'Clicked')+SUM(action = 'Viewed'))*100,2),0) AS ctrFROM AdsGROUP BY ad_idORDER BY ctr DESC,ad_id;


1327. 列出指定时间段内所有的下单产品

表: Products

+------------------+---------+
| Column Name | Type |
+------------------+---------+
| product_id | int |
| product_name | varchar |
| product_category | varchar |
+------------------+---------+
product_id 是该表主键。
该表包含该公司产品的数据。

表: Orders

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| product_id | int |
| order_date | date |
| unit | int |
+---------------+---------+
该表无主键,可能包含重复行。
product_id 是表单 Products 的外键。
unit 是在日期 order_date 内下单产品的数目。

写一个 SQL 语句,要求获取在 2020 年 2 月份下单的数量不少于 100 的产品的名字和数目。

返回结果表单的顺序无要求。

查询结果的格式如下:

Products 表:
+-------------+-----------------------+------------------+
| product_id | product_name | product_category |
+-------------+-----------------------+------------------+
| 1 | Leetcode Solutions | Book |
| 2 | Jewels of Stringology | Book |
| 3 | HP | Laptop |
| 4 | Lenovo | Laptop |
| 5 | Leetcode Kit | T-shirt |
+-------------+-----------------------+------------------+

Orders 表:
+--------------+--------------+----------+
| product_id | order_date | unit |
+--------------+--------------+----------+
| 1 | 2020-02-05 | 60 |
| 1 | 2020-02-10 | 70 |
| 2 | 2020-01-18 | 30 |
| 2 | 2020-02-11 | 80 |
| 3 | 2020-02-17 | 2 |
| 3 | 2020-02-24 | 3 |
| 4 | 2020-03-01 | 20 |
| 4 | 2020-03-04 | 30 |
| 4 | 2020-03-04 | 60 |
| 5 | 2020-02-25 | 50 |
| 5 | 2020-02-27 | 50 |
| 5 | 2020-03-01 | 50 |
+--------------+--------------+----------+

Result 表:
+--------------------+---------+
| product_name | unit |
+--------------------+---------+
| Leetcode Solutions | 130 |
| Leetcode Kit | 100 |
+--------------------+---------+

2020 年 2 月份下单 product_id = 1 的产品的数目总和为 (60 + 70) = 130 。
2020 年 2 月份下单 product_id = 2 的产品的数目总和为 80 。
2020 年 2 月份下单 product_id = 3 的产品的数目总和为 (2 + 3) = 5 。
2020 年 2 月份 product_id = 4 的产品并没有下单。
2020 年 2 月份下单 product_id = 5 的产品的数目总和为 (50 + 50) = 100 。
select product_name,sum(unit) unitfrom products p,orders owhere p.product_id=o.product_id and order_date between '2020-02-01' and '2020-02-29'group by product_namehaving sum(unit)>=100;


1336. 每次访问的交易次数

表: Visits

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| user_id | int |
| visit_date | date |
+---------------+---------+
(user_id, visit_date) 是该表的主键
该表的每行表示 user_id 在 visit_date 访问了银行

表: Transactions

+------------------+---------+
| Column Name | Type |
+------------------+---------+
| user_id | int |
| transaction_date | date |
| amount | int |
+------------------+---------+
该表没有主键,所以可能有重复行
该表的每一行表示 user_id 在 transaction_date 完成了一笔 amount 数额的交易
可以保证用户 (user) 在 transaction_date 访问了银行 (也就是说 Visits 表包含 (user_id, transaction_date) 行)

银行想要得到银行客户在一次访问时的交易次数和相应的在一次访问时该交易次数的客户数量的图表

写一条 SQL 查询多少客户访问了银行但没有进行任何交易,多少客户访问了银行进行了一次交易等等

结果包含两列:

  • transactions_count: 客户在一次访问中的交易次数

  • visits_count: 在 transactions_count 交易次数下相应的一次访问时的客户数量

transactions_count 的值从 0 到所有用户一次访问中的 max(transactions_count) 

按 transactions_count 排序

下面是查询结果格式的例子:

Visits 表:
+---------+------------+
| user_id | visit_date |
+---------+------------+
| 1 | 2020-01-01 |
| 2 | 2020-01-02 |
| 12 | 2020-01-01 |
| 19 | 2020-01-03 |
| 1 | 2020-01-02 |
| 2 | 2020-01-03 |
| 1 | 2020-01-04 |
| 7 | 2020-01-11 |
| 9 | 2020-01-25 |
| 8 | 2020-01-28 |
+---------+------------+
Transactions 表:
+---------+------------------+--------+
| user_id | transaction_date | amount |
+---------+------------------+--------+
| 1 | 2020-01-02 | 120 |
| 2 | 2020-01-03 | 22 |
| 7 | 2020-01-11 | 232 |
| 1 | 2020-01-04 | 7 |
| 9 | 2020-01-25 | 33 |
| 9 | 2020-01-25 | 66 |
| 8 | 2020-01-28 | 1 |
| 9 | 2020-01-25 | 99 |
+---------+------------------+--------+
结果表:
+--------------------+--------------+
| transactions_count | visits_count |
+--------------------+--------------+
| 0 | 4 |
| 1 | 5 |
| 2 | 0 |
| 3 | 1 |
+--------------------+--------------+
* 对于 transactions_count = 0, visits 中 (1, "2020-01-01"), (2, "2020-01-02"), (12, "2020-01-01") 和 (19, "2020-01-03") 没有进行交易,所以 visits_count = 4 。
* 对于 transactions_count = 1, visits 中 (2, "2020-01-03"), (7, "2020-01-11"), (8, "2020-01-28"), (1, "2020-01-02") 和 (1, "2020-01-04") 进行了一次交易,所以 visits_count = 5 。
* 对于 transactions_count = 2, 没有客户访问银行进行了两次交易,所以 visits_count = 0 。
* 对于 transactions_count = 3, visits 中 (9, "2020-01-25") 进行了三次交易,所以 visits_count = 1 。
* 对于 transactions_count >= 4, 没有客户访问银行进行了超过3次交易,所以我们停止在 transactions_count = 3 。

如下是这个例子的图表:

from (select max(c.transactions_count) over() mcnt, c.transactions_count, c.visits_count from (select transactions_count, count(user_id) visits_count from (select nvl(a.cnt, 0) transactions_count, b.user_id from (select user_id, transaction_date, count(1) cnt from Transactions1 group by user_id, transaction_date) a right join visits b on a.user_id = b.user_id and a.transaction_date = b.visit_date) group by transactions_count) c) c right join (select level - 1 lv from dual connect by level < 1000) d on c.transactions_count = d.lv

其中,

select level - 1 lv from dual connect by level < 1000

是用来生成0-998的序列。其他方法再琢磨琢磨。


1341. 电影评分

表:Movies

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| movie_id | int |
| title | varchar |
+---------------+---------+
movie_id 是这个表的主键。
title 是电影的名字。

表:Users

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| user_id | int |
| name | varchar |
+---------------+---------+
user_id 是表的主键。

表:Movie_Rating

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| movie_id | int |
| user_id | int |
| rating | int |
| created_at | date |
+---------------+---------+
(movie_id, user_id) 是这个表的主键。
这个表包含用户在其评论中对电影的评分 rating 。
created_at 是用户的点评日期。

请你编写一组 SQL 查询:

  • 查找评论电影数量最多的用户名。

    如果出现平局,返回字典序较小的用户名。

  • 查找在 2020 年 2 月 平均评分最高 的电影名称。

    如果出现平局,返回字典序较小的电影名称。

查询分两行返回,查询结果格式如下例所示:

Movies 表:
+-------------+--------------+
| movie_id | title |
+-------------+--------------+
| 1 | Avengers |
| 2 | Frozen 2 |
| 3 | Joker |
+-------------+--------------+

Users 表:
+-------------+--------------+
| user_id | name |
+-------------+--------------+
| 1 | Daniel |
| 2 | Monica |
| 3 | Maria |
| 4 | James |
+-------------+--------------+

Movie_Rating 表:
+-------------+--------------+--------------+-------------+
| movie_id | user_id | rating | created_at |
+-------------+--------------+--------------+-------------+
| 1 | 1 | 3 | 2020-01-12 |
| 1 | 2 | 4 | 2020-02-11 |
| 1 | 3 | 2 | 2020-02-12 |
| 1 | 4 | 1 | 2020-01-01 |
| 2 | 1 | 5 | 2020-02-17 |
| 2 | 2 | 2 | 2020-02-01 |
| 2 | 3 | 2 | 2020-03-01 |
| 3 | 1 | 3 | 2020-02-22 |
| 3 | 2 | 4 | 2020-02-25 |
+-------------+--------------+--------------+-------------+
Result 表:
+--------------+
| results |
+--------------+
| Daniel |
| Frozen 2 |
+--------------+
Daniel 和 Monica 都点评了 3 部电影("Avengers", "Frozen 2" 和 "Joker") 但是 Daniel 字典序比较小。
Frozen 2 和 Joker 在 2 月的评分都是 3.5,但是 Frozen 2 的字典序比较小。
select w.name as results from (select t.user_id,t1.name,count(t.movie_id),dense_rank() over(order by count(t.movie_id) desc,t1.name asc) rk  from MOVIE_RATING t join Users t1 on t.user_id=t1.user_id group by t.user_id,t1.name) wwhere w.rk=1union allselect q.title as results from (select a.movie_id,b.title,avg(a.rating),dense_rank() over(order by avg(a.rating) desc,b.title asc) rk  from MOVIE_RATING a join movies b on a.movie_id=b.movie_id where to_char(a.created_at,'yyyy-mm')='2020-02' group by a.movie_id,b.title) qwhere q.rk=1;

1.对用户名称和用户姓名进行分组,求出每个用户评论得电影数,用开窗函数求出第一名得那个,注意用平局得时候,按名称升序

2.同理再求出电影名称

3.用union all并集


1350. 院系无效的学生

院系表: Departments

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| id | int |
| name | varchar |
+---------------+---------+
id 是该表的主键
该表包含一所大学每个院系的 id 信息

学生表: Students

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| id | int |
| name | varchar |
| department_id | int |
+---------------+---------+
id 是该表的主键
该表包含一所大学每个学生的 id 和他/她就读的院系信息

写一条 SQL 语句以查询那些所在院系不存在的学生的 id 和姓名

可以以任何顺序返回结果

下面是返回结果格式的例子

Departments 表:
+------+--------------------------+
| id | name |
+------+--------------------------+
| 1 | Electrical Engineering |
| 7 | Computer Engineering |
| 13 | Bussiness Administration |
+------+--------------------------+

Students 表:
+------+----------+---------------+
| id | name | department_id |
+------+----------+---------------+
| 23 | Alice | 1 |
| 1 | Bob | 7 |
| 5 | Jennifer | 13 |
| 2 | John | 14 |
| 4 | Jasmine | 77 |
| 3 | Steve | 74 |
| 6 | Luis | 1 |
| 8 | Jonathan | 7 |
| 7 | Daiana | 33 |
| 11 | Madelynn | 1 |
+------+----------+---------------+

结果表:
+------+----------+
| id | name |
+------+----------+
| 2 | John |
| 7 | Daiana |
| 4 | Jasmine |
| 3 | Steve |
+------+----------+

John, Daiana, Steve 和 Jasmine 所在的院系分别是 14, 33, 74 和 77, 其中 14, 33, 74 和 77 并不存在于院系表
select id,namefrom studentswhere department_id not in (select id from departments);


1355. 活动参与者

表: Friends

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| id | int |
| name | varchar |
| activity | varchar |
+---------------+---------+
id 是朋友的 id 和该表的主键
name 是朋友的名字
activity 是朋友参加的活动的名字

表: Activities

+---------------+---------+
| Column Name | Type |
+---------------+---------+
| id | int |
| name | varchar |
+---------------+---------+
id 是该表的主键
name 是活动的名字

写一条 SQL 查询那些既没有最多,也没有最少参与者的活动的名字

可以以任何顺序返回结果,Activities 表的每项活动的参与者都来自 Friends 表

下面是查询结果格式的例子:

Friends 表:
+------+--------------+---------------+
| id | name | activity |
+------+--------------+---------------+
| 1 | Jonathan D. | Eating |
| 2 | Jade W. | Singing |
| 3 | Victor J. | Singing |
| 4 | Elvis Q. | Eating |
| 5 | Daniel A. | Eating |
| 6 | Bob B. | Horse Riding |
+------+--------------+---------------+

Activities 表:
+------+--------------+
| id | name |
+------+--------------+
| 1 | Eating |
| 2 | Singing |
| 3 | Horse Riding |
+------+--------------+

Result 表:
+--------------+
| activity |
+--------------+
| Singing |
+--------------+

Eating 活动有三个人参加, 是最多人参加的活动 (Jonathan D. , Elvis Q. and Daniel A.)
Horse Riding 活动有一个人参加, 是最少人参加的活动 (Bob B.)
Singing 活动有两个人参加 (Victor J. and Jade W.)
select activityfrom friendsgroup by activityhaving count(name) not in (select max(cn) from (select activity,count(name) cn from friends group by activity) a union all  select min(cn) from (select activity,count(name) cn from friends group by activity) a);

或者:

select name as ACTIVITY from Activitieswhere name not in  (select activity  from friends  group by activity  having count(id) >= all(select count(id)  from friends  group by activity))and name in (select activity  from friends  group by activity  having count(id) > any(select count(id)  from friends  group by activity));

未完待续。。。