二十三、SQL 数据分析实战(10个简单的SQL题目)
文章目录
题目1: 比赛名单整理
现有一张参加比赛的队伍名称表 easy_competition_list,easy_competition_list 表的数据如下所示:
mysql> select * from easy_competition_list;
-- team_name 参数队名
+------------+
| team_name |
+------------+
| 谁与争锋队 |
| 必胜队 |
| 乘风破浪队 |
| 群英汇队 |
| 梦之队 |
+------------+
5 rows in set (0.00 sec)
【题目1】每个参赛队伍都会和其他参赛队伍开展一次组队比赛,要求输出两两参赛队伍的所有比赛情况组合(两者分别为队伍A和队伍B),并按照队名依次升序排列。输出内容包括:队伍A、队伍B,结果样例图如下所示:
+------------+------------+
| 队伍A | 队伍B |
+------------+------------+
| 乘风破浪队 | 必胜队 |
| 乘风破浪队 | 梦之队 |
| 乘风破浪队 | 群英汇队 |
| 乘风破浪队 | 谁与争锋队 |
| 必胜队 | 梦之队 |
| 必胜队 | 群英汇队 |
| 必胜队 | 谁与争锋队 |
| 梦之队 | 群英汇队 |
| 梦之队 | 谁与争锋队 |
| 群英汇队 | 谁与争锋队 |
+------------+------------+
10 rows in set (0.00 sec)
【题目1解析】使用表的自连接,通过在队名之间以<的方式连接来确保队伍不会和自身匹配,并依次按照队名进行升序排列即可,本题的SQL代码如下:
mysql> SELECT a.team_name AS 队伍A,b.team_name AS 队伍B FROM easy_competition_list
-> a INNER JOIN easy_competition_list b ON a.team_name < b.team_name ORDER BY 队伍A,队伍B;
题目2: 热门游戏排行
现在有一张游戏下载量排行表 easy_game_ranking,easy_game_ranking 表的数据如下所示:
mysql> SELECT * FROM easy_game_ranking;
-- ① game: 游戏名称 VARCHAR ② category: 游戏类别 VARCHAR ③ downloads: 游戏下载量 INT
+------+----------+-----------+
| game | category | downloads |
+------+----------+-----------+
| A | puzzle | 13628 |
| B | shooting | 2830 |
| C | shooting | 1920 |
| D | action | 23800 |
| E | puzzle | 842 |
| F | shooting | 48201 |
| G | action | 4532 |
| H | puzzle | 1028 |
| I | action | 48910 |
| J | shooting | 342 |
| K | puzzle | 32456 |
| L | action | 2801 |
| M | puzzle | 1248 |
| N | action | 8756 |
+------+----------+-----------+
14 rows in set (0.00 sec)
【题目2】查询每个类别下载量排在前两名的游戏。输出内容包括:category(游戏类别)、game(游戏名称),结果样例如下图所示:
【题目2解析】典型组内排名问题,对于这个问题可以利用窗口函数来实现,利用DENSE_RANK()函数生成每个游戏类别内每个游戏的排序结果,最后通过排序结果筛选出我们所需要的数据,参考代码如下:
-- ① 使用窗口函数+分组聚合
mysql> SELECT category, GROUP_CONCAT(game) as game
-> FROM (SELECT *, DENSE_RANK() OVER (PARTITION BY category ORDER BY downloads DESC) AS 'downloads_rank'
-> FROM easy_game_ranking)
-> AS a
-> WHERE a.downloads_rank < 3
-> GROUP BY category;
-- ② 使用分组聚合+文本字符串处理函数
-- 通过分组统计,在每组内按照游戏下载量降序排列并使用GROUP_CONCAT()函数进行连接,然后使用
-- SUBSTRING_INDEX()函数提取出前两个游戏名称即可得到结果
mysql> SELECT category, SUBSTRING_INDEX(GROUP_CONCAT(game ORDER BY downloads DESC), ',', 2) AS game
-> FROM easy_game_ranking
-> GROUP BY category;
题目3: 社区生鲜App覆盖分析
现有一张用户安装的社区生鲜 App 表 easy_fresh_food,easy_fresh_food 表的数据如下所示:
mysql> SELECT * FROM easy_fresh_food;
-- user_id(用户ID): VARCHAR app(用户安装的社区生鲜App列表): VARCHAR
+---------+-------+
| user_id | app |
+---------+-------+
| u001 | A,B |
| u002 | C,D,A |
| u003 | E |
| u004 | A |
| u005 | F,D |
| u006 | E,G |
| u007 | C,B |
| u008 | H,J |
| u009 | J |
| u010 | A,K,E |
+---------+-------+
10 rows in set (0.00 sec)
【题目3】查询安装了 A
App的用户人数 。输出内容包括:num(用户人数),结果样例如下图所示:
【题目3解析】思路①: 模糊匹配,只要有A就计数,可以使用like关键字或者是mysql内置的函数FIND_IN_SET或者是INSTR。思路②: 按照 ,
进行分割,将一条数据拆分成多行数据,然后分组计数,参考代码如下:
-- 第①种写法: 模糊匹配使用LIKE或者是REGEXP关键字
mysql> SELECT COUNT(*) AS num FROM easy_fresh_food WHERE app LIKE '%A%';
mysql> SELECT COUNT(*) AS num FROM easy_fresh_food WHERE app REGEXP 'A';
-- 第②种写法: 模糊匹配使用mysql内置函数 FIND_IN_SET或者是INSTR
mysql> SELECT SUM(IF(FIND_IN_SET('A', app), 1, 0)) AS num
-> FROM easy_fresh_food;
mysql> SELECT SUM(CASE WHEN INSTR(app, 'A') > 0 THEN 1 ELSE 0 END) AS num
-> FROM easy_fresh_food;
-- 第③种写法: 先将一行数据拆分为多行 然后分组计算
-- 如果 mysql.help_topic 没有权限,可以自己创建一张临时表,用来与要查询的表连接查询
-- 创建临时表,并给临时表添加数据: 注意:
-- 1.临时表必须有一列从 0 或者 1 开始的自增数据
-- 2.临时表表名随意,字段可以只有一个
-- 3.临时表示的数据量必须比 (LENGTH(easy_fresh_food.app)-LENGTH(REPLACE(easy_fresh_food.app, ',', '')) + 1) 的值大
mysql> SELECT *
-> FROM (SELECT
-> SUBSTRING_INDEX(SUBSTRING_INDEX(easy_fresh_food.app, ',', b.help_topic_id + 1), ',', - 1) AS app_name,
-> COUNT(user_id) AS num
-> FROM easy_fresh_food
-> INNER JOIN mysql.help_topic b ON b.help_topic_id < (LENGTH(easy_fresh_food.app) -
-> LENGTH(REPLACE(easy_fresh_food.app, ',', '')) + 1)
-> WHERE `app` <> ''
-> GROUP BY app_name) a
-> WHERE a.app_name = 'A';
+----------+-----+
| app_name | num |
+----------+-----+
| A | 4 |
+----------+-----+
1 row in set (0.00 sec)
题目4: 社区团购行为分析
现有一张社区团购用户订单表 easy_group_buy,该表记录了用户在不同日期通过不同渠道登录团购页面和产生订单的情况,easy_group_buy 表的数据如下表所示:
mysql> SELECT * FROM easy_group_buy;
-- user_id(用户id):VARCHAR login_source(登录渠道):VARCHAR login_date(登录日期):DATE order_count(产生订单数量):INT
+---------+--------------+------------+-------------+
| user_id | login_source | login_date | order_count |
+---------+--------------+------------+-------------+
| a001 | applet | 2021-03-20 | 1 |
| a002 | application | 2021-03-20 | 0 |
| a003 | web | 2021-03-21 | 0 |
| a002 | application | 2021-03-21 | 2 |
| a001 | applet | 2021-03-21 | 4 |
| a003 | application | 2021-03-22 | 1 |
| a001 | applet | 2021-03-22 | 1 |
| a004 | application | 2021-03-23 | 1 |
+---------+--------------+------------+-------------+
8 rows in set (0.00 sec)
【题目4-1】查询每个用户首次登录的渠道名称。输出内容包括:user_id(用户ID)、login_source(登录渠道),结果样例如下图所示:
【题目4-1解析】思路①: 使用MIN()函数找出每个用户的最早登录时间,将原始表和每个用户的最早登录时间进行INNER JOIN,以获取用户ID和用户登录渠道。思路②: 窗口函数,参考代码如下:
-- 第①种写法
mysql> SELECT a1.user_id, a1.login_source
-> FROM easy_group_buy a1
-> INNER JOIN (SELECT user_id, MIN(login_date) AS first_login_date FROM easy_group_buy GROUP BY user_id) a2
-> ON a1.login_date = a2.first_login_date AND a1.user_id = a2.user_id;
-- 第②种写法
mysql> SELECT user_id, login_source
-> FROM (SELECT user_id, login_source, DENSE_RANK() OVER (PARTITION BY user_id ORDER BY login_date ASC ) AS login_date_rank
-> FROM easy_group_buy) temp_table
-> WHERE temp_table.login_date_rank = 1;
【题目4-2】查询用户登录日期和累计下单数量。输出内容包括:user_id(用户ID)、login_date(登录日期)、total_order_count(累计下单数量),结果样例如下图所示:
【题目4-2解析】使用SUM()函数,根据用户ID进行分组并根据登录日期排序,即可得到用户登录日期和累计下单数量,涉及知识点:窗口函数,参考代码如下:
mysql> SELECT user_id,
-> login_date,
-> SUM(order_count) OVER (PARTITION BY user_id ORDER BY login_date)
-> AS total_order_count
-> FROM easy_group_buy;
题目5: 统计字符出现次数
现有一张原始文本表 easy_original_text,easy_original_text 表的数据如下表所示:
mysql> SELECT * FROM easy_original_text;
-- text_id(文本ID): VARCHAR text_content(文本内容): VARCHAR
+---------+--------------+
| text_id | text_content |
+---------+--------------+
| t001 | !**@%& |
| t002 | * |
| t003 | @@!***&* |
| t004 | %&*$@ |
| t005 | ******* |
| t006 | 123456 |
+---------+--------------+
6 rows in set (0.00 sec)
【题目5】统计每条文本中符号出现的次数。输出内容包括:text_id(文本ID)、num(符号*出现的次数),结果样例如下图所示:
【题目5解析】使用REPLACE()函数将文本中的替换为空字符串(空字符串的长度为0),替换前后的文本长度之差就是文本中*出现的次数,涉及知识点:字符串处理函数,参考代码如下:
mysql> SELECT text_id, LENGTH(text_content) - LENGTH(REPLACE(text_content, '*', '')) AS num
-> FROM easy_original_text table1;
题目6: 找出各类别商品销量最高的商品
现有一张商品销量情况表 easy_product_sale,该表记录了不同类别商品销量的商品信息,easy_product_sale 表的数据如下表所示:
mysql> SELECT * FROM easy_product_sale;
-- product_id: 商品ID VARCHAR product_category: 商品类别 VARCHAR sale: 商品销量 INT
+------------+------------------+-------+
| product_id | product_category | sale |
+------------+------------------+-------+
| p001 | c001 | 14600 |
| p002 | c001 | 23300 |
| p003 | c001 | 8000 |
| p004 | c002 | 40800 |
| p005 | c002 | 5300 |
| p006 | c003 | 12900 |
+------------+------------------+-------+
6 rows in set (0.00 sec)
【题目6】查询不同类别商品销量最高的商品信息。输出内容包括:product_category(商品类别)、product_id(商品ID)、sale(商品销量),结果样例如下图所示:
【题目6解析】使用DENSE_RANK()函数生成新的一列,即不同商品类别的销量排名(sale_rank),然后将该部分作为子查询内部,在子查询外部通过WHERE筛选出sale_rank=1的记录,即可得到不同类别商品销量最高的商品信息,参考代码如下:
mysql> SELECT temp_table.product_category, temp_table.product_id, temp_table.sale
-> FROM (SELECT *, DENSE_RANK() OVER (PARTITION BY product_category ORDER BY sale DESC ) AS sale_rank
-> FROM easy_product_sale) temp_table
-> WHERE temp_table.sale_rank = 1;
题目7: 找出每个部门薪资第二高的员工
现有一张公司员工信息表 easy_employee,easy_employee 表的数据如下表所示:
mysql> SELECT * FROM easy_employee;
-- employee_id(员工ID): VARCHAR employee_name(员工姓名): VARCHAR employee_salary(员工薪资): INT
-- department(员工所属部门ID): VARCHAR
+-------------+---------------+-----------------+------------+
| employee_id | employee_name | employee_salary | department |
+-------------+---------------+-----------------+------------+
| a001 | Bob | 7000 | b1 |
| a002 | Jack | 9000 | b1 |
| a003 | Alice | 8000 | b2 |
| a004 | Ben | 5000 | b2 |
| a005 | Candy | 4000 | b2 |
| a006 | Allen | 5000 | b2 |
| a007 | Linda | 10000 | b3 |
+-------------+---------------+-----------------+------------+
7 rows in set (0.00 sec)
还有一张部门信息表 easy_department,easy_department 表的数据如下表所示:
mysql> SELECT * FROM easy_department;
-- department_id(部门ID): VARCHAR department_name(部门名称): VARCHAR
+---------------+-----------------+
| department_id | department_name |
+---------------+-----------------+
| b1 | Sales |
| b2 | IT |
| b3 | Product |
+---------------+-----------------+
3 rows in set (0.00 sec)
【题目7】查询每个部门薪资第二高的员工信息。输出内容包括:employee_id (员工ID)、employee_name(员工姓名)、employee_salary(员工薪资)、department_name(员工所属部门名称),结果样例如下图所示:
【题目7解析】使用窗口函数,根据部门ID分组在组内按照员工薪资降序排列并记为employee_salary_rank,使用employee_salary_rank=2作为薪资第二高的条件进行WHERE筛选,然后将该处理后的表和部门信息表进行内连接,从而把部门名称关联进来,选择需要的列,即可得到结果,参考代码如下:
mysql> SELECT a2.employee_id, a2.employee_name, a2.employee_salary, easy_department.department_name
-> FROM (SELECT *
-> FROM (SELECT *, RANK() OVER (PARTITION BY department ORDER BY employee_salary DESC ) AS employee_salary_rank
-> FROM easy_employee) AS a1
-> WHERE a1.employee_salary_rank = 2) AS a2
-> INNER JOIN easy_department ON a2.department = easy_department.department_id;
题目8: 游戏玩家登录情况分析
现有一张游戏玩家登录情况表 easy_game_login,easy_game_login 表的数据如下表所示:
mysql> SELECT * FROM easy_game_login;
-- user_id(玩家ID): VARCHAR login_time(登录时间): VARCHAR
+---------+---------------------+
| user_id | login_time |
+---------+---------------------+
| u001 | 2021-03-01 06:01:12 |
| u001 | 2021-03-01 07:14:20 |
| u002 | 2021-03-01 07:20:22 |
| u003 | 2021-03-01 08:22:45 |
| u001 | 2021-03-01 11:10:23 |
| u004 | 2021-03-01 12:00:10 |
| u002 | 2021-03-01 18:03:52 |
| u005 | 2021-03-01 20:10:29 |
| u003 | 2021-03-01 21:11:50 |
+---------+---------------------+
9 rows in set (0.00 sec)
【题目8-1】查询一天中多次登录游戏的玩家及其登录的次数。输出内容包括:user_id(玩家ID)、login_date(登录日期)、num(登录次数),结果样例如下图所示:
【题目8-1解析】对字符串格式的时间进行转换处理,使用LEFT()函数截取日期部分,并通过分组聚合使用HAVING筛选出一天内多次登录的玩家,参考代码如下:
mysql> SELECT a.user_id, a.login_date, COUNT(a.login_date) AS 'num'
-> FROM (SELECT user_id, LEFT(login_time, 10) AS 'login_date' FROM easy_game_login)
-> AS a
-> GROUP BY a.user_id, a.login_date
-> HAVING COUNT(a.login_date) > 1;
【题目8-2】对于在一天中多次登录游戏的玩家,只查找当天最后一条记录。输出内容包括:user_id(玩家ID)、login_time(登录时间),结果样例如下图所示:
【题目8-2解析】在上一题找出的一天内多次登录的玩家的基础上,使用RANK()函数根据用户分组并按照时间排序选出最后一条记录。参考代码如下:
mysql> SELECT user_id, login_time
-> FROM (SELECT e1.user_id,
-> e1.login_time,
-> RANK() OVER (PARTITION BY e1.user_id,LEFT(login_time, 10) ORDER BY login_time DESC ) AS login_time_rank
-> FROM easy_game_login e1
-> INNER JOIN (
-> SELECT a.user_id, a.login_date
-> FROM (SELECT user_id, LEFT(login_time, 10) AS 'login_date' FROM easy_game_login)
-> AS a
-> GROUP BY a.user_id, a.login_date
-> HAVING COUNT(a.login_date) > 1) e2 ON e1.user_id = e2.user_id AND left(e1.login_time, 10) = e2.login_date) b
-> WHERE b.login_time_rank = 1;
题目9: 用户首单消费金额
现有一张用户在电商网站的购物订单信息表 easy_user_order,该表记录了用户购物等相关信息,easy_user_order 表的数据如下表所示:
mysql> SELECT * FROM easy_user_order;
-- user_id(用户ID):VARCHAR payment(订单金额):INT paytime(下单时间):DATETIME
+---------+---------+---------------------+
| user_id | payment | paytime |
+---------+---------+---------------------+
| a001 | 500 | 2021-02-01 13:25:00 |
| a001 | 800 | 2021-02-03 09:10:00 |
| b001 | 150 | 2021-02-03 15:18:00 |
| a002 | 90 | 2021-02-05 08:10:00 |
| a001 | 1050 | 2021-02-06 10:34:00 |
| b001 | 400 | 2021-02-07 18:19:00 |
+---------+---------+---------------------+
6 rows in set (0.00 sec)
【题目9】用户在电商网站的首单(下单时间最早的订单)可以反映出用户的消费能力,要求统计每个用户的首单信息。输出内容包括:user_id(用户ID)、payment(订单金额),结果样例如下图所示:
【题目9解析】使用DENSE_RANK()函数,根据用户ID进行分组并按照下单时间默认升序排列以得到每个用户各自的下单时间排名,在子查询外部查询并筛选出每个用户排名为1的订单信息(即首单信息)涉及知识点:子查询、窗口函数。参考代码如下:
mysql> SELECT user_id, payment
-> FROM (SELECT user_id, payment, DENSE_RANK() OVER (PARTITION BY user_id ORDER BY paytime ASC) AS 'paytime_rank'
-> FROM easy_user_order)
-> AS a
-> WHERE a.paytime_rank = 1;
题目10: 参与优惠活动的商品
现在有一张商品优惠活动时间表 easy_product_promotion,easy_product_promotion 表的数据如下所示:
mysql> SELECT * FROM easy_product_promotion;
-- commodity_id(商品ID):VARCHAR start_date(商品优惠活动起始日期):DATE end_date(商品优惠活动结束日期):DATE
+--------------+------------+------------+
| commodity_id | start_date | end_date |
+--------------+------------+------------+
| a001 | 2021-01-01 | 2021-01-06 |
| a002 | 2021-01-01 | 2021-01-10 |
| a003 | 2021-01-02 | 2021-01-07 |
| a004 | 2021-01-05 | 2021-01-07 |
| b001 | 2021-01-05 | 2021-01-10 |
| b002 | 2021-01-04 | 2021-01-06 |
| c001 | 2021-01-06 | 2021-01-08 |
| c002 | 2021-01-02 | 2021-01-04 |
| c003 | 2021-01-08 | 2021-01-15 |
+--------------+------------+------------+
9 rows in set (0.00 sec)
【题目10】查询在2021年1月7日至2021年1月9日期间参与优惠活动的商品。输出内容包括:commodity_id(商品ID)结果样例如下图所示:
【解析10】本题适合采用图解法将所有可能的时间排列先枚举出来,再进行SQL代码编写。假设2021年1月7日为时间a,2021年1月9日为时间b,每个优惠活动的开始时间为s,结束时间为e,则所有可能的序列为“sabe”、“saeb”、“asbe”、和“aseb”。涉及知识点:复杂的时间判断。本题的SQL代码如下:
mysql> SELECT commodity_id FROM easy_product_promotion
-> WHERE (start_date <= '2021-01-09' AND start_date >= '2021-01-07')
-> OR (end_date <= '2021-01-09' AND end_date >='2021-01-07')
-> OR (start_date >= '2021-01-07' AND end_date <= '2021-01-09')
-> OR (start_date <= '2021-01-07' AND end_date >= '2021-01-09');
至此今天的学习就到此结束了,笔者在这里声明,笔者写文章只是为了学习交流,以及让更多学习数据库的读者少走一些弯路,节省时间,并不用做其他用途,如有侵权,联系博主删除即可。感谢您阅读本篇博文,希望本文能成为您编程路上的领航者。祝您阅读愉快!
好书不厌读百回,熟读课思子自知。而我想要成为全场最靓的仔,就必须坚持通过学习来获取更多知识,用知识改变命运,用博客见证成长,用行动证明我在努力。
如果我的博客对你有帮助、如果你喜欢我的博客内容,请点赞
、评论
、收藏
一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。
编码不易,大家的支持就是我坚持下去的动力。点赞后不要忘了关注
我哦!