41. 买下所有产品的用户

需求:编写一个 SQL 查询,从 Customer 表中查询购买了 Product 表中所有产品的客户的 id。

展示效果:

customer_id
1
3
  1. Create table If Not Exists 41_Customer (customer_id int, product_key int);
  2. Create table 41_Product (product_key int);
  3. Truncate table 41_Customer;
  4. insert into 41_Customer (customer_id, product_key) values (1, 5);
  5. insert into 41_Customer (customer_id, product_key) values (2, 6);
  6. insert into 41_Customer (customer_id, product_key) values (3, 5);
  7. insert into 41_Customer (customer_id, product_key) values (3, 6);
  8. insert into 41_Customer (customer_id, product_key) values (1, 6);
  9. Truncate table 41_Product;
  10. insert into 41_Product (product_key) values (5);
  11. insert into 41_Product (product_key) values (6);

最终SQL:

  1. select
  2. customer_id
  3. from
  4. (select
  5. customer_id,
  6. count(distinct product_key) as num
  7. from
  8. 41_Customer
  9. group by
  10. customer_id) t
  11. join
  12. (select
  13. count(product_key) as num
  14. from
  15. 41_Product) m
  16. on t.num = m.num;

42. 合作过至少三次的演员和导演

需求:编写一个 SQL 查询,查询语句获取合作过至少三次的演员和导演的 id 对 (actor_id, director_id)

展示效果:

actor_id director_id
1 1
  1. Create table If Not Exists 42_ActorDirector (actor_id int, director_id int, timestamp int);
  2. Truncate table 42_ActorDirector;
  3. insert into 42_ActorDirector (actor_id, director_id, timestamp) values (1, 1, 0);
  4. insert into 42_ActorDirector (actor_id, director_id, timestamp) values (1, 1, 1);
  5. insert into 42_ActorDirector (actor_id, director_id, timestamp) values (1, 1, 2);
  6. insert into 42_ActorDirector (actor_id, director_id, timestamp) values (1, 2, 3);
  7. insert into 42_ActorDirector (actor_id, director_id, timestamp) values (1, 2, 4);
  8. insert into 42_ActorDirector (actor_id, director_id, timestamp) values (2, 1, 5);
  9. insert into 42_ActorDirector (actor_id, director_id, timestamp) values (2, 1, 6);

最终SQL:

  1. select
  2. actor_id,
  3. director_id
  4. from
  5. 42_ActorDirector
  6. group by
  7. actor_id,director_id
  8. having
  9. count(*)>=3;

43. 产品销售分析

需求一:获取产品表 Product 中所有的 产品名称 product name 以及 该产品在 Sales 表中相对应的 上市年份 year价格 price

展示效果:

product_name year price
Nokia 2008 5000
Nokia 2009 5000
Apple 2011 9000
  1. Create table If Not Exists 43_Sales (sale_id int, product_id int, year int, quantity int, price int);
  2. Create table If Not Exists 43_Product (product_id int, product_name varchar(10));
  3. Truncate table 43_Sales;
  4. insert into 43_Sales (sale_id, product_id, year, quantity, price) values (1, 100, 2008, 10, 5000);
  5. insert into 43_Sales (sale_id, product_id, year, quantity, price) values (2, 100, 2009, 12, 5000);
  6. insert into 43_Sales (sale_id, product_id, year, quantity, price) values (7, 200, 2011, 15, 9000);
  7. Truncate table 43_Product;
  8. insert into 43_Product (product_id, product_name) values (100, 'Nokia');
  9. insert into 43_Product (product_id, product_name) values (200, 'Apple');
  10. insert into 43_Product (product_id, product_name) values (300, 'Samsung');

最终SQL:

  1. select
  2. product_name,
  3. year,
  4. price
  5. from
  6. 43_Sales
  7. inner join
  8. 43_Product
  9. on
  10. 43_Sales.product_id = 43_Product.product_id;

需求二:按产品 id(product_id )来统计每个产品的销售总量。

展示效果:

product_id total_quantity
100 22
200 15

最终SQL:

  1. SELECT
  2. product_id,
  3. SUM(quantity) as total_quantity
  4. FROM
  5. 43_Sales
  6. GROUP BY
  7. product_id;

需求三:选出每个销售产品的第一年 的 产品 id、年份、数量 和 价格。

展示效果:

product_id first_year quantity price
100 2008 10 5000
200 2011 15 9000

最终SQL:

  1. select
  2. product_id,
  3. year as first_year,
  4. quantity,
  5. price
  6. from
  7. 43_Sales
  8. where
  9. (product_id , year) in(
  10. select
  11. product_id ,
  12. min(year)
  13. from
  14. 43_Sales
  15. group by
  16. product_id
  17. );

44. 项目员工

需求一:查询每一个项目中员工的平均工作年限,精确到小数点后两位。

展示效果:

project_id average_years
1 2.00
2 2.50
  1. Create table If Not Exists 44_Project (project_id int, employee_id int);
  2. Create table If Not Exists 44_Employee (employee_id int, name varchar(10), experience_years int);
  3. Truncate table 44_Project;
  4. insert into 44_Project (project_id, employee_id) values (1, 1);
  5. insert into 44_Project (project_id, employee_id) values (1, 2);
  6. insert into 44_Project (project_id, employee_id) values (1, 3);
  7. insert into 44_Project (project_id, employee_id) values (2, 1);
  8. insert into 44_Project (project_id, employee_id) values (2, 4);
  9. Truncate table 44_Employee;
  10. insert into 44_Employee (employee_id, name, experience_years) values (1, 'Khaled', 3);
  11. insert into 44_Employee (employee_id, name, experience_years) values (2, 'Ali', 2);
  12. insert into 44_Employee (employee_id, name, experience_years) values (3, 'John', 1);
  13. insert into 44_Employee (employee_id, name, experience_years) values (4, 'Doe', 2);

最终SQL:

  1. select
  2. project_id ,
  3. round(avg(experience_years),2) as average_years
  4. from
  5. 44_Project p
  6. left join
  7. 44_Employee e
  8. on
  9. p.employee_id = e.employee_id
  10. group by
  11. project_id
  12. order by
  13. project_id;

需求二:报告所有雇员最多的项目。

展示效果:

project_id
1

最终SQL:

  1. SELECT
  2. project_id
  3. FROM
  4. 44_Project
  5. GROUP BY
  6. project_id
  7. HAVING
  8. COUNT(employee_id) = (SELECT
  9. MAX(count_employee_id)
  10. FROM
  11. (SELECT
  12. project_id,
  13. COUNT(employee_id) AS count_employee_id
  14. FROM
  15. 44_Project
  16. GROUP BY
  17. project_id
  18. ) As temp
  19. );

需求三:报告在每一个项目中经验最丰富的雇员是谁。如果出现经验年数相同的情况,请报告所有具有最大经验年数的员工。

展示效果:

project_id employee_id
1 1
2 1

最终SQL:

  1. select
  2. p.project_id,
  3. p.employee_id
  4. from
  5. 44_Project p
  6. join
  7. 44_Employee e
  8. on
  9. p.employee_id = e.employee_id
  10. where
  11. (p.project_id, e.experience_years) in (select
  12. p.project_id,
  13. max(e.experience_years)
  14. from
  15. 44_project p
  16. join
  17. 44_employee e
  18. on
  19. p.employee_id = e.employee_id
  20. group by
  21. p.project_id
  22. );

45. 销售分析

需求一:编写一个 SQL 查询,查询总销售额最高的销售者,如果有并列的,就都展示出来。

展示效果:

seller_id
1
3
  1. Create table If Not Exists 45_Product (product_id int, product_name varchar(10), unit_price int);
  2. Create table If Not Exists 45_Sales (seller_id int, product_id int,buyer_id int, sale_date date, quantity int, price int);
  3. Truncate table 45_Product;
  4. insert into 45_Product (product_id, product_name, unit_price) values (1, 'S8', 1000);
  5. insert into 45_Product (product_id, product_name, unit_price) values (2, 'G4', 800);
  6. insert into 45_Product (product_id, product_name, unit_price) values (3, 'iPhone', 1400);
  7. Truncate table 45_Sales;
  8. insert into 45_Sales (seller_id, product_id, buyer_id, sale_date, quantity, price) values (1, 1, 1,'2019-01-21', 2, 2000);
  9. insert into 45_Sales (seller_id, product_id, buyer_id, sale_date, quantity, price) values (1, 2, 2,'2019-02-17', 1, 800);
  10. insert into 45_Sales (seller_id, product_id, buyer_id, sale_date, quantity, price) values (2, 1, 3,'2019-06-02', 1, 800);
  11. insert into 45_Sales (seller_id, product_id, buyer_id, sale_date, quantity, price) values (3, 3, 3,'2019-05-13', 2, 2800);

最终SQL:

  1. select
  2. seller_id
  3. from
  4. 45_Sales
  5. group by
  6. seller_id
  7. having
  8. sum(price) = (select
  9. sum(price) as ye_ji
  10. from
  11. 45_Sales
  12. group by
  13. seller_id
  14. order by
  15. ye_ji desc
  16. limit 1
  17. );

需求二:编写一个 SQL 查询,查询购买了 S8 手机却没有购买 iPhone 的买家。注意这里 S8 和 iPhone 是 Product 表中的产品。

展示效果:

buyer_id
1

最终SQL:

  1. -- 方法一
  2. select
  3. distinct buyer_id
  4. from
  5. 45_product p
  6. inner join
  7. 45_sales s
  8. on
  9. p.product_id=s.product_id
  10. where
  11. product_name='S8' and buyer_id not in (select
  12. buyer_id
  13. from
  14. 45_product p
  15. inner join
  16. 45_sales s
  17. on
  18. p.product_id=s.product_id
  19. where
  20. product_name='iPhone'
  21. );
  22. -- 方法二
  23. select
  24. s8 as buyer_id
  25. from
  26. (select
  27. distinct buyer_id s8
  28. from
  29. 45_product p
  30. inner join
  31. 45_sales s
  32. on
  33. p.product_id=s.product_id
  34. where
  35. product_name='S8') t1
  36. left join
  37. (select
  38. distinct buyer_id ip
  39. from
  40. 45_product p
  41. inner join
  42. 45_sales s
  43. on
  44. p.product_id=s.product_id
  45. where
  46. product_name='iPhone'
  47. ) t2
  48. on
  49. s8=ip
  50. where
  51. ip is null;

需求三:编写一个 SQL 查询,报告2019年仅在春季才售出的产品。即在2019-01-01至2019-03-31(含)之间。

展示效果:

product_id product_name
2 G4

最终SQL:

  1. SELECT
  2. DISTINCT s.product_id,
  3. p.product_name
  4. FROM
  5. 45_Sales AS s
  6. INNER JOIN
  7. 45_Product AS p
  8. ON
  9. s.product_id = p.product_id
  10. WHERE
  11. s.product_id NOT IN(
  12. SELECT
  13. DISTINCT product_id
  14. FROM
  15. 45_Sales
  16. WHERE
  17. sale_date NOT BETWEEN '2019-01-01' AND '2019-03-31');

46. 小众书籍

需求:筛选出过去一年中订单总量少于10本的书籍 。注意:不考虑上架(available from)距今不满一个月的书籍。并且假设今天是2019-06-23 。

展示效果:

book_id name
1 Kalila And Demna
2 28 Letters
5 The Hunger Games
  1. Create table If Not Exists 46_Books (book_id int, name varchar(50), available_from date);
  2. Create table If Not Exists 46_Orders (order_id int, book_id int, quantity int, dispatch_date date);
  3. Truncate table 46_Books;
  4. insert into 46_Books (book_id, name, available_from) values (1, 'Kalila And Demna', '2010-01-01');
  5. insert into 46_Books (book_id, name, available_from) values (2, '28 Letters', '2012-05-12');
  6. insert into 46_Books (book_id, name, available_from) values (3, 'The Hobbit', '2019-06-10');
  7. insert into 46_Books (book_id, name, available_from) values (4, '13 Reasons Why', '2019-06-01');
  8. insert into 46_Books (book_id, name, available_from) values (5, 'The Hunger Games', '2008-09-21');
  9. Truncate table 46_Orders;
  10. insert into 46_Orders (order_id, book_id, quantity, dispatch_date) values (1, 1, 2, '2018-07-26');
  11. insert into 46_Orders (order_id, book_id, quantity, dispatch_date) values (2, 1, 1, '2018-11-05');
  12. insert into 46_Orders (order_id, book_id, quantity, dispatch_date) values (3, 3, 8, '2019-06-11');
  13. insert into 46_Orders (order_id, book_id, quantity, dispatch_date) values (4, 4, 6, '2019-06-05');
  14. insert into 46_Orders (order_id, book_id, quantity, dispatch_date) values (5, 4, 5, '2019-06-20');
  15. insert into 46_Orders (order_id, book_id, quantity, dispatch_date) values (6, 5, 9, '2009-02-02');
  16. insert into 46_Orders (order_id, book_id, quantity, dispatch_date) values (7, 5, 8, '2010-04-13');

最终SQL:

  1. select
  2. t1.book_id,
  3. (select name from 46_books t3 where t1.book_id = t3.book_id) as name
  4. from
  5. (select
  6. *
  7. from
  8. 46_books
  9. where
  10. available_from < date_sub('2019-06-23',interval 1 Month)
  11. ) t1
  12. left join
  13. (select
  14. *,
  15. case
  16. when dispatch_date between '2018-06-23' and '2019-06-23' then quantity
  17. else 0
  18. end num
  19. from 46_orders
  20. ) t2
  21. on
  22. t1.book_id=t2.book_id
  23. group by
  24. t1.book_id
  25. having
  26. sum(if(t2.num is null,0,t2.num))<10;

47. 每日新用户统计

需求一:编写一个 SQL 查询,以查询从今天起最多 90 天内,每个日期内首次登录的用户数。假设今天是 2019-06-30.

展示效果:

login_date user_count
2019-05-01 1
2019-06-21 2
  1. Create table If Not Exists 47_Traffic (user_id int, activity ENUM('login', 'logout', 'jobs', 'groups', 'homepage'), activity_date date);
  2. Truncate table 47_Traffic;
  3. insert into 47_Traffic (user_id, activity, activity_date) values (1, 'login', '2019-05-01');
  4. insert into 47_Traffic (user_id, activity, activity_date) values (1, 'homepage', '2019-05-01');
  5. insert into 47_Traffic (user_id, activity, activity_date) values (1, 'logout', '2019-05-01');
  6. insert into 47_Traffic (user_id, activity, activity_date) values (2, 'login', '2019-06-21');
  7. insert into 47_Traffic (user_id, activity, activity_date) values (2, 'logout', '2019-06-21');
  8. insert into 47_Traffic (user_id, activity, activity_date) values (3, 'login', '2019-01-01');
  9. insert into 47_Traffic (user_id, activity, activity_date) values (3, 'jobs', '2019-01-01');
  10. insert into 47_Traffic (user_id, activity, activity_date) values (3, 'logout', '2019-01-01');
  11. insert into 47_Traffic (user_id, activity, activity_date) values (4, 'login', '2019-06-21');
  12. insert into 47_Traffic (user_id, activity, activity_date) values (4, 'groups', '2019-06-21');
  13. insert into 47_Traffic (user_id, activity, activity_date) values (4, 'logout', '2019-06-21');
  14. insert into 47_Traffic (user_id, activity, activity_date) values (5, 'login', '2019-03-01');
  15. insert into 47_Traffic (user_id, activity, activity_date) values (5, 'logout', '2019-03-01');
  16. insert into 47_Traffic (user_id, activity, activity_date) values (5, 'login', '2019-06-21');
  17. insert into 47_Traffic (user_id, activity, activity_date) values (5, 'logout', '2019-06-21');

最终SQL:

  1. select
  2. minx as login_date,
  3. count(user_id) as user_count
  4. from
  5. (select
  6. user_id,
  7. min(activity_date) as minx
  8. from
  9. 47_Traffic
  10. where
  11. activity='login'
  12. group by
  13. user_id
  14. having
  15. datediff('2019-06-30',minx)<=90
  16. )s
  17. group by
  18. minx;

48. 每位学生的最高成绩

需求:查询每位学生获得的最高成绩和它所对应的科目,若科目成绩并列,取 course_id 最小的一门。查询结果需按 student_id 增序进行排序。

展示效果:

student_id course_id grade
1 2 99
2 2 95
3 3 82
  1. Create table If Not Exists 48_Enrollments (student_id int, course_id int, grade int);
  2. Truncate table 48_Enrollments;
  3. insert into 48_Enrollments (student_id, course_id, grade) values (2, 2, 95);
  4. insert into 48_Enrollments (student_id, course_id, grade) values (2, 3, 95);
  5. insert into 48_Enrollments (student_id, course_id, grade) values (1, 1, 90);
  6. insert into 48_Enrollments (student_id, course_id, grade) values (1, 2, 99);
  7. insert into 48_Enrollments (student_id, course_id, grade) values (3, 1, 80);
  8. insert into 48_Enrollments (student_id, course_id, grade) values (3, 2, 75);
  9. insert into 48_Enrollments (student_id, course_id, grade) values (3, 3, 82);

最终SQL:

  1. select
  2. e1.student_id,
  3. min(e1.course_id) as course_id,
  4. max(e1.grade) as grade
  5. from
  6. 48_Enrollments e1
  7. right join
  8. (select
  9. student_id,
  10. max(grade) as max1
  11. from
  12. 48_Enrollments
  13. group by
  14. student_id
  15. )e2
  16. on
  17. e2.student_id=e1.student_id
  18. and
  19. e2.max1 = e1.grade
  20. group by
  21. e1.student_id
  22. order by
  23. e1.student_id;

49. 举报记录

需求一: 编写一条SQL,查询昨天不同举报类型的数量,假设今天是 2019-07-05。 在action 列标记为report 被认为遭到举报,对应的产生举报原因。

展示效果:

report_reason report_count
spam 1
  1. Create table If Not Exists 49_Actions (user_id int, post_id int, action_date date, action ENUM('view', 'like', 'reaction', 'comment', 'report', 'share'), extra varchar(10));
  2. Truncate table 49_Actions;
  3. insert into 49_Actions (user_id, post_id, action_date, action, extra) values (1, 1, '2019-07-01', 'view', null);
  4. insert into 49_Actions (user_id, post_id, action_date, action, extra) values (1, 1, '2019-07-01', 'like', null);
  5. insert into 49_Actions (user_id, post_id, action_date, action, extra) values (1, 1, '2019-07-01', 'share', null);
  6. insert into 49_Actions (user_id, post_id, action_date, action, extra) values (2, 4, '2019-07-04', 'view', null);
  7. insert into 49_Actions (user_id, post_id, action_date, action, extra) values (2, 4, '2019-07-04', 'report', 'spam');
  8. insert into 49_Actions (user_id, post_id, action_date, action, extra) values (3, 4, '2019-07-04', 'view', null);
  9. insert into 49_Actions (user_id, post_id, action_date, action, extra) values (3, 4, '2019-07-04', 'report', 'spam');
  10. insert into 49_Actions (user_id, post_id, action_date, action, extra) values (4, 3, '2019-07-02', 'view', null);
  11. insert into 49_Actions (user_id, post_id, action_date, action, extra) values (4, 3, '2019-07-02', 'report', 'spam');
  12. insert into 49_Actions (user_id, post_id, action_date, action, extra) values (5, 2, '2019-07-03', 'view', null);
  13. insert into 49_Actions (user_id, post_id, action_date, action, extra) values (5, 2, '2019-07-03', 'report', 'racism');
  14. insert into 49_Actions (user_id, post_id, action_date, action, extra) values (5, 5, '2019-07-03', 'view', null);
  15. insert into 49_Actions (user_id, post_id, action_date, action, extra) values (5, 5, '2019-07-03', 'report', 'racism');

最终SQL:

  1. select
  2. extra report_reason,
  3. count(distinct post_id) report_count
  4. from
  5. 49_Actions
  6. where
  7. datediff('2019-07-05', action_date) = 1
  8. and
  9. extra is not null
  10. and
  11. action = 'report'
  12. group by
  13. extra;

50. 查询活跃业务

需求:写一段 SQL 来查询所有活跃的业务。如果一个业务的某个事件类型的发生次数大于此事件类型在所有业务中的平均发生次数,并且该业务至少有两个这样的事件类型,那么该业务就可被看做是活跃业务。

展示效果:

business_id
1

reviews、 ads 和 page views 的总平均发生次数分别是 (7+3)/2=5, (11+7+6)/3=8, (3+12)/2=7.5。
id 为 1 的业务有 7 个 ‘reviews’ 事件(大于 5)和 11 个 ‘ads’ 事件(大于 8),所以它是活跃业务。

  1. Create table If Not Exists 50_Events (business_id int, event_type varchar(10), occurences int);
  2. Truncate table 50_Events;
  3. insert into 50_Events (business_id, event_type, occurences) values (1, 'reviews', 7);
  4. insert into 50_Events (business_id, event_type, occurences) values (3, 'reviews', 3);
  5. insert into 50_Events (business_id, event_type, occurences) values (1, 'ads', 11);
  6. insert into 50_Events (business_id, event_type, occurences) values (2, 'ads', 7);
  7. insert into 50_Events (business_id, event_type, occurences) values (3, 'ads', 6);
  8. insert into 50_Events (business_id, event_type, occurences) values (1, 'page views', 3);
  9. insert into 50_Events (business_id, event_type, occurences) values (2, 'page views', 12);

最终SQL:

  1. select
  2. e2.business_id
  3. from
  4. 50_events e2
  5. join
  6. (select
  7. event_type,
  8. avg(occurences) as avg_occ
  9. from
  10. 50_events
  11. group by
  12. event_type) e1
  13. on
  14. e2.event_type = e1.event_type
  15. and
  16. e2.occurences > e1.avg_occ
  17. group by
  18. e2.business_id
  19. having
  20. count(business_id) >=2;

51. 用户购买平台

需求一: 写一段 SQL 来查找每天 使用手机端用户、 使用桌面端用户和 同时 使用桌面端和手机端的用户人数和总支出金额。

展示效果:

spend_date platform total_amount total_users
2019-07-01 desktop 100 1
2019-07-01 mobile 100 1
2019-07-01 both 200 1
2019-07-02 desktop 100 1
2019-07-02 mobile 100 1
2019-07-02 both 0 0
  1. Create table If Not Exists 51_Spending (user_id int, spend_date date, platform ENUM('desktop', 'mobile'), amount int);
  2. Truncate table 51_Spending;
  3. insert into 51_Spending (user_id, spend_date, platform, amount) values (1, '2019-07-01', 'mobile', 100);
  4. insert into 51_Spending (user_id, spend_date, platform, amount) values (1, '2019-07-01', 'desktop', 100);
  5. insert into 51_Spending (user_id, spend_date, platform, amount) values (2, '2019-07-01', 'mobile', 100);
  6. insert into 51_Spending (user_id, spend_date, platform, amount) values (2, '2019-07-02', 'mobile', 100);
  7. insert into 51_Spending (user_id, spend_date, platform, amount) values (3, '2019-07-01', 'desktop', 100);
  8. insert into 51_Spending (user_id, spend_date, platform, amount) values (3, '2019-07-02', 'desktop', 100);

在 2019-07-01, 用户1 同时 使用桌面端和手机端购买, 用户2 仅 使用了手机端购买,而用户3 仅 使用了桌面端购买。

在 2019-07-02, 用户2 仅 使用了手机端购买, 用户3 仅 使用了桌面端购买,且没有用户 同时 使用桌面端和手机端购买。

最终SQL:

  1. select
  2. temp1.spend_date,
  3. temp1.platform,
  4. ifnull(temp3.total_amount, 0) total_amount,
  5. ifnull(temp3.total_users,0) total_users
  6. from
  7. (select
  8. distinct(spend_date),
  9. p.platform
  10. from
  11. 51_Spending,
  12. (select
  13. 'desktop' as platform union
  14. select
  15. 'mobile' as platform union
  16. select
  17. 'both' as platform
  18. ) as p
  19. ) as temp1
  20. left join
  21. (select
  22. spend_date,
  23. platform,
  24. sum(amount) as total_amount,
  25. count(user_id) total_users
  26. from
  27. (select
  28. spend_date,
  29. user_id,
  30. case count(distinct platform)
  31. when 1 then platform
  32. when 2 then 'both'
  33. end as platform,
  34. sum(amount) as amount
  35. from
  36. 51_Spending
  37. group by
  38. spend_date,
  39. user_id
  40. ) as temp2
  41. group by
  42. spend_date,
  43. platform
  44. ) as temp3
  45. on
  46. temp1.platform = temp3.platform and
  47. temp1.spend_date = temp3.spend_date;

52. 查询近30天活跃用户

需求一:请写SQL查询出截至 2019-07-27(包含2019-07-27),近 30天的每日活跃用户数(当天只要有一条活动记录,即为活跃用户)。

展示效果:

day active_users
2019-07-20 2
2019-07-21 2
  1. Create table If Not Exists 52_Activity (user_id int, session_id int, activity_date date, activity_type ENUM('open_session', 'end_session', 'scroll_down', 'send_message'));
  2. Truncate table 52_Activity;
  3. insert into 52_Activity (user_id, session_id, activity_date, activity_type) values (1, 1, '2019-07-20', 'open_session');
  4. insert into 52_Activity (user_id, session_id, activity_date, activity_type) values (1, 1, '2019-07-20', 'scroll_down');
  5. insert into 52_Activity (user_id, session_id, activity_date, activity_type) values (1, 1, '2019-07-20', 'end_session');
  6. insert into 52_Activity (user_id, session_id, activity_date, activity_type) values (2, 4, '2019-07-20', 'open_session');
  7. insert into 52_Activity (user_id, session_id, activity_date, activity_type) values (2, 4, '2019-07-21', 'send_message');
  8. insert into 52_Activity (user_id, session_id, activity_date, activity_type) values (2, 4, '2019-07-21', 'end_session');
  9. insert into 52_Activity (user_id, session_id, activity_date, activity_type) values (3, 2, '2019-07-21', 'open_session');
  10. insert into 52_Activity (user_id, session_id, activity_date, activity_type) values (3, 2, '2019-07-21', 'send_message');
  11. insert into 52_Activity (user_id, session_id, activity_date, activity_type) values (3, 2, '2019-07-21', 'end_session');
  12. insert into 52_Activity (user_id, session_id, activity_date, activity_type) values (4, 3, '2019-06-25', 'open_session');
  13. insert into 52_Activity (user_id, session_id, activity_date, activity_type) values (4, 3, '2019-06-25', 'end_session');

最终SQL:

  1. select
  2. t.activity_date as day,
  3. count(distinct t.user_id) as active_users
  4. from
  5. (select
  6. activity_date,
  7. user_id
  8. from
  9. 52_Activity
  10. where
  11. datediff('2019-07-27',activity_date) <30
  12. and
  13. datediff( '2019-07-27', activity_date) >=0
  14. group by
  15. user_id,
  16. activity_date
  17. having
  18. count(activity_type)>0
  19. ) as t
  20. group by
  21. t.activity_date;

需求二:编写SQL查询以查找截至2019年7月27日(含)的30天内每个用户的平均会话数,四舍五入到小数点后两位。我们要为用户计算的会话是在该时间段内至少进行了一项活动的会话。

展示效果:

average_sessions_per_user
1.00

最终SQL:

  1. SELECT
  2. ROUND(IFNULL(AVG(count_session_id), 0), 2) AS average_sessions_per_user
  3. FROM
  4. (SELECT
  5. COUNT(DISTINCT session_id) AS count_session_id
  6. FROM
  7. 52_Activity
  8. WHERE
  9. activity_date BETWEEN DATE_SUB("2019-07-27", INTERVAL 29 DAY) AND "2019-07-27"
  10. GROUP BY
  11. user_id
  12. ) AS temp;

53. 文章浏览

需求一:找出所有浏览过自己文章的作者,结果按照 id 升序排列。

展示效果:

id
4
7
  1. Create table If Not Exists 53_Views (article_id int, author_id int, viewer_id int, view_date date);
  2. Truncate table 53_Views;
  3. insert into 53_Views (article_id, author_id, viewer_id, view_date) values (1, 3, 5, '2019-08-01');
  4. insert into 53_Views (article_id, author_id, viewer_id, view_date) values (3, 4, 5, '2019-08-01');
  5. insert into 53_Views (article_id, author_id, viewer_id, view_date) values (1, 3, 6, '2019-08-02');
  6. insert into 53_Views (article_id, author_id, viewer_id, view_date) values (2, 7, 7, '2019-08-01');
  7. insert into 53_Views (article_id, author_id, viewer_id, view_date) values (2, 7, 6, '2019-08-02');
  8. insert into 53_Views (article_id, author_id, viewer_id, view_date) values (4, 7, 1, '2019-07-22');
  9. insert into 53_Views (article_id, author_id, viewer_id, view_date) values (3, 4, 4, '2019-07-21');
  10. insert into 53_Views (article_id, author_id, viewer_id, view_date) values (3, 4, 4, '2019-07-21');

最终SQL:

  1. select
  2. distinct viewer_id as id
  3. from
  4. 53_Views
  5. where
  6. viewer_id = author_id
  7. order by
  8. viewer_id;

需求二:找出在同一天阅读至少两篇文章的人,结果按照 id 升序排序。

展示效果:

project_id
5
6

最终SQL:

  1. SELECT
  2. DISTINCT viewer_id as id
  3. FROM
  4. 53_views
  5. GROUP BY
  6. viewer_id,view_date
  7. HAVING
  8. COUNT(DISTINCT article_id)>=2
  9. ORDER BY
  10. viewer_id;

54. 市场分析

需求一: 请写出一条SQL语句以查询每个用户的注册日期和在 2019 年作为买家的订单总数。

展示效果:

buyer_id join_date orders_in_2019
1 2018-01-01 1
2 2018-02-09 2
3 2018-01-19 0
4 2018-05-21 0
  1. Create table If Not Exists 54_Users (user_id int, join_date date, favorite_brand varchar(10));
  2. create table if not exists 54_Orders (order_id int, order_date date, item_id int, buyer_id int, seller_id int);
  3. create table if not exists 54_Items (item_id int, item_brand varchar(10));
  4. Truncate table 54_Users;
  5. insert into 54_Users (user_id, join_date, favorite_brand) values (1, '2018-01-01', 'Lenovo');
  6. insert into 54_Users (user_id, join_date, favorite_brand) values (2, '2018-02-09', 'Samsung');
  7. insert into 54_Users (user_id, join_date, favorite_brand) values (3, '2018-01-19', 'LG');
  8. insert into 54_Users (user_id, join_date, favorite_brand) values (4, '2018-05-21', 'HP');
  9. Truncate table 54_Orders;
  10. insert into 54_Orders (order_id, order_date, item_id, buyer_id, seller_id) values (1, '2019-08-01', 4, 1, 2);
  11. insert into 54_Orders (order_id, order_date, item_id, buyer_id, seller_id) values (2, '2018-08-02', 2, 1, 3);
  12. insert into 54_Orders (order_id, order_date, item_id, buyer_id, seller_id) values (3, '2019-08-03', 3, 2, 3);
  13. insert into 54_Orders (order_id, order_date, item_id, buyer_id, seller_id) values (4, '2018-08-04', 1, 4, 2);
  14. insert into 54_Orders (order_id, order_date, item_id, buyer_id, seller_id) values (5, '2018-08-04', 1, 3, 4);
  15. insert into 54_Orders (order_id, order_date, item_id, buyer_id, seller_id) values (6, '2019-08-05', 2, 2, 4);
  16. Truncate table 54_Items;
  17. insert into 54_Items (item_id, item_brand) values (1, 'Samsung');
  18. insert into 54_Items (item_id, item_brand) values (2, 'Lenovo');
  19. insert into 54_Items (item_id, item_brand) values (3, 'LG');
  20. insert into 54_Items (item_id, item_brand) values (4, 'HP');

最终SQL:

  1. SELECT
  2. user_id AS buyer_id,
  3. join_date,
  4. IFNULL(COUNT(buyer_Id), 0) AS orders_in_2019
  5. FROM
  6. 54_Users u
  7. LEFT JOIN
  8. 54_Orders o
  9. ON
  10. U.user_id = o.buyer_id
  11. AND
  12. order_date >= '2019-01-01'
  13. GROUP BY
  14. user_id
  15. ORDER BY
  16. user_id;

需求二: 写一个 SQL 查询确定每一个用户按日期顺序卖出的第二件商品的品牌是否是他们最喜爱的品牌。如果一个用户卖出少于两件商品,查询的结果是 no

展示效果:

seller_id 2nd_item_fav_brand
1 no
2 no
3 yes
4 no

最终SQL:

  1. -- 方法一
  2. select
  3. user_id as seller_id,
  4. if (r2.item_brand is null || r2.item_brand != favorite_brand, "no", "yes") as 2nd_item_fav_brand
  5. from
  6. 54_users
  7. left join
  8. (select
  9. r1.seller_id,
  10. i.item_brand
  11. from
  12. (select
  13. @rk := if (@seller = a.seller_id, @rk + 1, 1) as `rank`,
  14. @seller := a.seller_id as seller_id,
  15. a.item_id
  16. from
  17. (select
  18. seller_id,
  19. item_id
  20. from
  21. 54_orders
  22. order by
  23. seller_id, order_date) a,
  24. (select @seller := -1, @rk := 0) b
  25. ) r1
  26. join
  27. 54_items i
  28. on
  29. r1.item_id = i.item_id
  30. where
  31. r1.`rank` = 2) r2
  32. on
  33. user_id = r2.seller_id;
  34. -- 方法二:
  35. select
  36. user_id seller_id,
  37. if(favorite_brand = item_brand, 'yes', 'no') 2nd_item_fav_brand
  38. from
  39. 54_users
  40. left join
  41. (select
  42. o1.seller_id,
  43. item_brand
  44. from
  45. 54_orders o1
  46. join
  47. 54_orders o2
  48. on
  49. o1.seller_id = o2.seller_id
  50. join
  51. 54_items i
  52. on
  53. o1.item_id = i.item_id
  54. group by
  55. o1.order_id
  56. having
  57. sum(o1.order_date > o2.order_date) = 1
  58. ) tmp
  59. on user_id = seller_id;
  60. -- 方法三:
  61. select
  62. u.seller_id,
  63. if(favorite_brand = item_brand, 'yes', 'no') as 2nd_item_fav_brand
  64. from
  65. (select user_id as seller_id from 54_Users) u
  66. left join
  67. (select
  68. *
  69. from
  70. (select
  71. o.order_date,
  72. o.seller_id,
  73. i.item_brand,
  74. u.favorite_brand,
  75. rank() over(partition by o.seller_id order by o.order_date) rnk
  76. from
  77. 54_Orders o
  78. left join
  79. 54_Users u
  80. on
  81. o.seller_id = u.user_id
  82. left join
  83. 54_Items i
  84. on
  85. o.item_id = i.item_id
  86. ) t1
  87. where rnk = 2
  88. ) t2
  89. on
  90. u.seller_id = t2.seller_id

55. 指定日期产品价格

需求一: 写一段 SQL来查找在 2019-08-16 时全部产品的价格,假设所有产品在修改前的价格都是 10。

展示效果:

project_id price
2 50
1 35
3 10
  1. Create table If Not Exists 55_Products (product_id int, new_price int, change_date date);
  2. Truncate table 55_Products;
  3. insert into 55_Products (product_id, new_price, change_date) values (1, 20, '2019-08-14');
  4. insert into 55_Products (product_id, new_price, change_date) values (2, 50, '2019-08-14');
  5. insert into 55_Products (product_id, new_price, change_date) values (1, 30, '2019-08-15');
  6. insert into 55_Products (product_id, new_price, change_date) values (1, 35, '2019-08-16');
  7. insert into 55_Products (product_id, new_price, change_date) values (2, 65, '2019-08-17');
  8. insert into 55_Products (product_id, new_price, change_date) values (3, 20, '2019-08-18');

最终SQL:

SELECT
    * 
FROM 
    (SELECT 
           product_id,
           new_price AS price
     FROM 
           55_Products
     WHERE (product_id, change_date) IN (
                                          SELECT
                                                product_id,
                                                MAX(change_date)
                                          FROM 
                                                55_Products
                                          WHERE 
                                                change_date <= '2019-08-16'
                                          GROUP BY
                                                product_id
                                         )
     UNION
     SELECT
            DISTINCT product_id, 10 AS price
     FROM 
            55_Products
     WHERE 
            product_id NOT IN (SELECT
                                     product_id
                               FROM  
                                     55_Products
                               WHERE change_date <= '2019-08-16'
                              )
     ) tmp
ORDER BY 
     price DESC;

56. 即时食物配送

需求一:查询语句获取即时订单所占的百分比, 保留两位小数。

展示效果:

immediate_percentage
42.86
Create table If Not Exists 56_Delivery (delivery_id int, customer_id int, order_date date, customer_pref_delivery_date date);
Truncate table 56_Delivery;
insert into 56_Delivery (delivery_id, customer_id, order_date, customer_pref_delivery_date) values (1, 1, '2019-08-01', '2019-08-02');
insert into 56_Delivery (delivery_id, customer_id, order_date, customer_pref_delivery_date) values (2, 5, '2019-08-02', '2019-08-02');
insert into 56_Delivery (delivery_id, customer_id, order_date, customer_pref_delivery_date) values (3, 1, '2019-08-11', '2019-08-11');
insert into 56_Delivery (delivery_id, customer_id, order_date, customer_pref_delivery_date) values (4, 3, '2019-08-24', '2019-08-26');
insert into 56_Delivery (delivery_id, customer_id, order_date, customer_pref_delivery_date) values (5, 4, '2019-08-21', '2019-08-22');
insert into 56_Delivery (delivery_id, customer_id, order_date, customer_pref_delivery_date) values (6, 2, '2019-08-11', '2019-08-13');
insert into 56_Delivery (delivery_id, customer_id, order_date, customer_pref_delivery_date) values (7, 4, '2019-08-09', '2019-08-09');

最终SQL:

SELECT ROUND(
    (SELECT
           COUNT(delivery_id)
    FROM 
           56_Delivery
    WHERE 
           order_date = customer_pref_delivery_date
    ) * 100 / COUNT(delivery_id) , 2) AS immediate_percentage
FROM 
    56_Delivery;

需求二:查询语句获取即时订单在所有用户的首次订单中的比例。保留两位小数。

展示效果:

immediate_percentage
40.00

最终SQL:

select
      round(
               count(case when d.order_date = d.customer_pref_delivery_date then 1 end)
               * 
               100/count(*), 2) as immediate_percentage
from 
     56_Delivery d,
    (select
           delivery_id,
           customer_id,
           min(order_date) as order_date
     from
           56_Delivery
     group by
           customer_id
    ) as t
where
     d.customer_id = t.customer_id
     and d.order_date = t.order_date;

57. 重新格式化部门表

需求一:编写一个 SQL 查询来重新格式化表,使得新的表中有一个部门 id 列和一些对应 每个月 的收入(revenue)列。

展示效果:

id Jan_Revenue Feb_Revenue Mar_Revenue Dec_Revenue
1 8000 7000 6000 null
2 9000 null null null
3 null 10000 null null
Create table If Not Exists 57_Department (id int, revenue int, month varchar(5));
Truncate table 57_Department;
insert into 57_Department (id, revenue, month) values (1, 8000, 'Jan');
insert into 57_Department (id, revenue, month) values (2, 9000, 'Jan');
insert into 57_Department (id, revenue, month) values (3, 10000, 'Feb');
insert into 57_Department (id, revenue, month) values (1, 7000, 'Feb');
insert into 57_Department (id, revenue, month) values (1, 6000, 'Mar');

最终SQL:

SELECT 
      DISTINCT id AS "id",
      SUM(IF (month = "Jan", revenue, null)) AS "Jan_Revenue",
      SUM(IF (month = "Feb", revenue, null)) AS "Feb_Revenue",
      SUM(IF (month = "Mar", revenue, null)) AS "Mar_Revenue",
      SUM(IF (month = "Apr", revenue, null)) AS "Apr_Revenue",
      SUM(IF (month = "May", revenue, null)) AS "May_Revenue",
      SUM(IF (month = "Jun", revenue, null)) AS "Jun_Revenue",
      SUM(IF (month = "Jul", revenue, null)) AS "Jul_Revenue",
      SUM(IF (month = "Aug", revenue, null)) AS "Aug_Revenue",
      SUM(IF (month = "Sep", revenue, null)) AS "Sep_Revenue",
      SUM(IF (month = "Oct", revenue, null)) AS "Oct_Revenue",
      SUM(IF (month = "Nov", revenue, null)) AS "Nov_Revenue",
      SUM(IF (month = "Dec", revenue, null)) AS "Dec_Revenue" 
FROM 
      57_Department
GROUP BY id;

58. 每月交易

需求一:查找每个月和每个国家/地区的事务数及其总金额、已批准的事务数及其总金额。

展示效果:

month country trans_count approved_count trans_total_amount approved_total_amount
2018-12 US 2 1 3000 1000
2019-01 US 1 1 2000 2000
2019-01 DE 1 1 2000 2000
2019-05 US 2 1 3000 1000
2019-06 US 3 2 12000 8000
create table if not exists 58_Transactions (id int, country varchar(4), state enum('approved', 'declined'), amount int, trans_date date);
create table if not exists 58_Chargebacks (trans_id int, trans_date date);
truncate table 58_Transactions;
insert into 58_Transactions (id, country, state, amount, trans_date) values (101, 'US', 'approved', 1000, '2018-12-18');
insert into 58_Transactions (id, country, state, amount, trans_date) values (102, 'US', 'declined', 2000, '2018-12-19');
insert into 58_Transactions (id, country, state, amount, trans_date) values (103, 'US', 'approved', 2000, '2019-01-01');
insert into 58_Transactions (id, country, state, amount, trans_date) values (104, 'DE', 'approved', 2000, '2019-01-07');
insert into 58_Transactions (id, country, state, amount, trans_date) values (105, 'US', 'approved', 1000, '2019-05-18');
insert into 58_Transactions (id, country, state, amount, trans_date) values (106, 'US', 'declined', 2000, '2019-05-19');
insert into 58_Transactions (id, country, state, amount, trans_date) values (107, 'US', 'approved', 3000, '2019-06-10');
insert into 58_Transactions (id, country, state, amount, trans_date) values (108, 'US', 'declined', 4000, '2019-06-13');
insert into 58_Transactions (id, country, state, amount, trans_date) values (109, 'US', 'approved', 5000, '2019-06-15');
truncate table 58_Chargebacks;
insert into 58_Chargebacks (trans_id, trans_date) values (102, '2019-05-29');
insert into 58_Chargebacks (trans_id, trans_date) values (101, '2019-06-30');
insert into 58_Chargebacks (trans_id, trans_date) values (105, '2019-09-18');

最终SQL:

select
      date_format(trans_date,'%Y-%m') as month,
      country,
      count(*) as trans_count,
      sum(if(state='approved',1,0)) as approved_count,
      sum(amount) as trans_total_amount,
      sum(if(state='approved',amount,0)) as approved_total_amount
from 
      58_Transactions t
group by
      date_format(trans_date,'%Y-%m'),
      country;

需求二:编写一个 SQL 查询,以查找每个月和每个国家/地区的已批准交易的数量及其总金额、退单的数量及其总金额。

展示效果:

month country approved_count approved_amount chargeback_count chargeback_amount
2018-12 US 1 1000 0 0
2019-01 DE 1 2000 0 0
2019-01 US 1 2000 0 0
2019-05 US 1 1000 1 2000
2019-06 US 2 8000 1 1000
2019-09 US 0 0 1 1000

最终SQL:

SELECT
       month as MONTH,
       country as COUNTRY,
       SUM(IF(type = 'approved', 1, 0)) AS APPROVED_COUNT,
       SUM(IF(type = 'approved', amount, 0)) AS APPROVED_AMOUNT,
       SUM(IF(type = 'chargeback', 1, 0)) AS CHARGEBACK_COUNT,
       SUM(IF(type = 'chargeback', amount, 0)) AS CHARGEBACK_AMOUNT
FROM 
      (SELECT 
              date_format(t.trans_date,'%Y-%m') AS month,
              t.country,
              amount,
              'approved' AS type
        FROM
              58_Transactions AS t
        WHERE 
              state = 'approved'
        UNION ALL
        SELECT
              date_format(c.trans_date,'%Y-%m') AS month,
              t.country,
              amount,
              'chargeback' AS type
         FROM 
              58_Transactions AS t
         INNER JOIN
              58_Chargebacks AS c 
         ON t.id = c.trans_id
         ) AS tt
GROUP BY 
         tt.month,
         tt.country;

59. 锦标赛优胜者

需求一:编写一个 SQL 查询来查找每组中的获胜者。每组的获胜者是在组内得分最高的选手。如果平局,player_id 最小的的选手获胜。

展示效果:

group_id player_id
1 15
2 35
3 40
Create table If Not Exists 59_Players (player_id int, group_id int);
Create table If Not Exists 59_Matches (match_id int, first_player int, second_player int, first_score int, second_score int);
Truncate table 59_Players;
insert into 59_Players (player_id, group_id) values (10, 2);
insert into 59_Players (player_id, group_id) values (15, 1);
insert into 59_Players (player_id, group_id) values (20, 3);
insert into 59_Players (player_id, group_id) values (25, 1);
insert into 59_Players (player_id, group_id) values (30, 1);
insert into 59_Players (player_id, group_id) values (35, 2);
insert into 59_Players (player_id, group_id) values (40, 3);
insert into 59_Players (player_id, group_id) values (45, 1);
insert into 59_Players (player_id, group_id) values (50, 2);
Truncate table 59_Matches;
insert into 59_Matches (match_id, first_player, second_player, first_score, second_score) values (1, 15, 45, 3, 0);
insert into 59_Matches (match_id, first_player, second_player, first_score, second_score) values (2, 30, 25, 1, 2);
insert into 59_Matches (match_id, first_player, second_player, first_score, second_score) values (3, 30, 15, 2, 0);
insert into 59_Matches (match_id, first_player, second_player, first_score, second_score) values (4, 40, 20, 5, 2);
insert into 59_Matches (match_id, first_player, second_player, first_score, second_score) values (5, 35, 50, 1, 1);

最终SQL:

select 
      group_id,
      max(total_scores) max_scores
from 
    (select 
           group_id,
           player_id,
           sum(
               case
                   when player_id = first_player then first_score
                   when player_id = second_player then second_score
               end
               ) as total_scores
     from 
          59_Players p,
          59_Matches m
     where
          p.player_id = m.first_player or
          p.player_id = m.second_player
     group by
          group_id,
          player_id
     order by
          group_id,
          total_scores desc,
          player_id
    ) as temp
group by 
     group_id

order by 
     group_id,
     total_scores desc,
     player_id;

60. 最后一个能进入电梯的人

需求:查找最后一个能进入电梯且不超过重量限制的 person_name 。题目确保队列中第一位的人可以进入电梯 。

展示效果:

person_name
Thomas Jefferson
Create table If Not Exists 60_Queue (person_id int, person_name varchar(30), weight int, turn int);
Truncate table 60_Queue;
insert into 60_Queue (person_id, person_name, weight, turn) values (5, 'George Washington', 250, 1);
insert into 60_Queue (person_id, person_name, weight, turn) values (4, 'Thomas Jefferson', 175, 5);
insert into 60_Queue (person_id, person_name, weight, turn) values (3, 'John Adams', 350, 2);
insert into 60_Queue (person_id, person_name, weight, turn) values (6, 'Thomas Jefferson', 400, 3);
insert into 60_Queue (person_id, person_name, weight, turn) values (1, 'James Elephant', 500, 6);
insert into 60_Queue (person_id, person_name, weight, turn) values (2, 'Will Johnliams', 200, 4);

最终SQL:

select 
      person_name
from 
      60_Queue q1
where 
     (select
           sum(weight)
      from
           60_Queue q
      where turn <= q1.turn) <= 1000
order by 
      turn desc 
limit 1;