From 4d33005c73de26721243c95f4d544f49040bf796 Mon Sep 17 00:00:00 2001 From: Guide Date: Fri, 14 Jul 2023 00:02:23 +0800 Subject: [PATCH] =?UTF-8?q?[docs=20update]markdown=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/database/sql/sql-questions-02.md | 32 ++++++------- docs/database/sql/sql-questions-03.md | 68 ++++++++++++--------------- docs/database/sql/sql-questions-04.md | 38 +++++++-------- docs/database/sql/sql-questions-05.md | 62 ++++++++++++------------ 4 files changed, 97 insertions(+), 103 deletions(-) diff --git a/docs/database/sql/sql-questions-02.md b/docs/database/sql/sql-questions-02.md index 546b6173..2bdbc994 100644 --- a/docs/database/sql/sql-questions-02.md +++ b/docs/database/sql/sql-questions-02.md @@ -95,7 +95,7 @@ REPLACE INTO examination_info VALUES ### 更新记录(一) -**描述:**现在有一张试卷信息表 `examination_info`, 表结构如下图所示: +**描述**:现在有一张试卷信息表 `examination_info`, 表结构如下图所示: | Filed | Type | Null | Key | Extra | Default | Comment | | ------------ | -------- | ---- | ---- | -------------- | ------- | -------- | @@ -140,9 +140,9 @@ SET tag = REPLACE(tag,'PYTHON','Python') **题目要求**:请把 `exam_record` 表中 2021 年 9 月 1 日==之前==开始作答的==未完成==记录全部改为被动完成,即:将完成时间改为'2099-01-01 00:00:00',分数改为 0。 -**思路:**注意题干中的关键字(已经高亮) `" xxx 时间 "`之前这个条件, 那么这里马上就要想到要进行时间的比较 可以直接 `xxx_time < "2021-09-01 00:00:00",` 也可以采用`date()`函数来进行比较;第二个条件就是 `"未完成"`, 即完成时间为 NULL,也就是题目中的提交时间 ----- `submit_time 为 NULL`。 +**思路**:注意题干中的关键字(已经高亮) `" xxx 时间 "`之前这个条件, 那么这里马上就要想到要进行时间的比较 可以直接 `xxx_time < "2021-09-01 00:00:00",` 也可以采用`date()`函数来进行比较;第二个条件就是 `"未完成"`, 即完成时间为 NULL,也就是题目中的提交时间 ----- `submit_time 为 NULL`。 -**答案:** +**答案**: ```sql UPDATE exam_record SET submit_time = '2099-01-01 00:00:00', score = 0 WHERE DATE(start_time) < "2021-09-01" AND submit_time IS null @@ -196,7 +196,7 @@ YEAR:年 这题需要进行分钟的比较,那么就是 TIMESTAMPDIFF(MINUTE, 开始时间, 结束时间) < 5 -**答案:** +**答案**: ```sql DELETE FROM exam_record WHERE MINUTE (TIMEDIFF(submit_time , start_time)) < 5 AND score < 60 @@ -301,7 +301,7 @@ TRUNCATE exam_record; | job | varchar(32) | YES | | (NULL) | | 职业方向 | | register_time | datetime | YES | | CURRENT_TIMESTAMP | | 注册时间 | -**思路:**如果这题给出了旧表的名称,可直接`create table 新表 as select * from 旧表;` 但是这题并没有给出旧表名称,所以需要自己创建,注意默认值和键的创建即可,比较简单。(注意:如果是在牛客网上面执行,请注意 comment 中要和题目中的 comment 保持一致,包括大小写,否则不通过,还有字符也要设置) +**思路**:如果这题给出了旧表的名称,可直接`create table 新表 as select * from 旧表;` 但是这题并没有给出旧表名称,所以需要自己创建,注意默认值和键的创建即可,比较简单。(注意:如果是在牛客网上面执行,请注意 comment 中要和题目中的 comment 保持一致,包括大小写,否则不通过,还有字符也要设置) 答案: @@ -335,7 +335,7 @@ CREATE TABLE IF NOT EXISTS user_info_vip( **要求:**请在用户信息表,字段 `level` 的后面增加一列最多可保存 15 个汉字的字段 `school`;并将表中 `job` 列名改为 `profession`,同时 `varchar` 字段长度变为 10;`achievement` 的默认值设置为 0。 -**思路:**首先做这题之前,需要了解 ALTER 语句的基本用法: +**思路**:首先做这题之前,需要了解 ALTER 语句的基本用法: - 添加一列:`ALTER TABLE 表名 ADD COLUMN 列名 类型 【first | after 字段名】;`(first : 在某列之前添加,after 反之) - 修改列的类型或约束:`ALTER TABLE 表名 MODIFY COLUMN 列名 新类型 【新约束】;` @@ -348,7 +348,7 @@ CREATE TABLE IF NOT EXISTS user_info_vip( 在修改时,如果有多个修改项,可以写到一起,但要注意格式 -**答案:** +**答案**: ```sql ALTER TABLE user_info @@ -359,13 +359,13 @@ ALTER TABLE user_info ### 删除表 -**描述:**现有一张试卷作答记录表 `exam_record`,其中包含多年来的用户作答试卷记录。一般每年都会为 `exam_record` 表建立一张备份表 `exam_record_{YEAR},{YEAR}` 为对应年份。 +**描述**:现有一张试卷作答记录表 `exam_record`,其中包含多年来的用户作答试卷记录。一般每年都会为 `exam_record` 表建立一张备份表 `exam_record_{YEAR},{YEAR}` 为对应年份。 现在随着数据越来越多,存储告急,请你把很久前的(2011 到 2014 年)备份表都删掉(如果存在的话)。 -**思路:**这题很简单,直接删就行,如果嫌麻烦,可以将要删除的表用逗号隔开,写到一行;这里肯定会有小伙伴问:如果要删除很多张表呢?放心,如果要删除很多张表,可以写脚本来进行删除。 +**思路**:这题很简单,直接删就行,如果嫌麻烦,可以将要删除的表用逗号隔开,写到一行;这里肯定会有小伙伴问:如果要删除很多张表呢?放心,如果要删除很多张表,可以写脚本来进行删除。 -**答案:**: +**答案**: ```sql DROP TABLE IF EXISTS exam_record_2011; @@ -376,7 +376,7 @@ DROP TABLE IF EXISTS exam_record_2014; ### 创建索引 -**描述:**现有一张试卷信息表 `examination_info`,其中包含各种类型试卷的信息。为了对表更方便快捷地查询,需要在 `examination_info` 表创建以下索引, +**描述**:现有一张试卷信息表 `examination_info`,其中包含各种类型试卷的信息。为了对表更方便快捷地查询,需要在 `examination_info` 表创建以下索引, 规则如下:在 `duration` 列创建普通索引 `idx_duration`、在 `exam_id` 列创建唯一性索引 `uniq_idx_exam_id`、在 `tag` 列创建全文索引 `full_idx_tag`。 @@ -390,7 +390,7 @@ DROP TABLE IF EXISTS exam_record_2014; 备注:后台会通过 `SHOW INDEX FROM examination_info` 语句来对比输出结果 -**思路:**做这题首先需要了解常见的索引类型: +**思路**:做这题首先需要了解常见的索引类型: - B-Tree 索引:B-Tree(或称为平衡树)索引是最常见和默认的索引类型。它适用于各种查询条件,可以快速定位到符合条件的数据。B-Tree 索引适用于普通的查找操作,支持等值查询、范围查询和排序。 - 唯一索引:唯一索引与普通的 B-Tree 索引类似,不同之处在于它要求被索引的列的值是唯一的。这意味着在插入或更新数据时,MySQL 会验证索引列的唯一性。 @@ -413,7 +413,7 @@ DROP TABLE IF EXISTS exam_record_2014; 有了以上的基础知识之后,该题答案也就浮出水面了。 -**答案:**: +**答案**: ```sql ALTER TABLE examination_info @@ -424,9 +424,9 @@ ALTER TABLE examination_info ### 删除索引 -**描述:**请删除`examination_info`表上的唯一索引 uniq_idx_exam_id 和全文索引 full_idx_tag。 +**描述**:请删除`examination_info`表上的唯一索引 uniq_idx_exam_id 和全文索引 full_idx_tag。 -**思路:**该题考察删除索引的基本语法: +**思路**:该题考察删除索引的基本语法: ```sql -- 使用 DROP INDEX 删除索引 @@ -440,7 +440,7 @@ ALTER TABLE employees DROP INDEX idx_email; 而且 **DROP** 命令需要慎用!!! -**答案:** +**答案**: ```sql DROP INDEX uniq_idx_exam_id ON examination_info; diff --git a/docs/database/sql/sql-questions-03.md b/docs/database/sql/sql-questions-03.md index 2244a38c..2511e0ab 100644 --- a/docs/database/sql/sql-questions-03.md +++ b/docs/database/sql/sql-questions-03.md @@ -14,7 +14,7 @@ tag: ### SQL 类别高难度试卷得分的截断平均值(较难) -**描述:**牛客的运营同学想要查看大家在 SQL 类别中高难度试卷的得分情况。 +**描述**: 牛客的运营同学想要查看大家在 SQL 类别中高难度试卷的得分情况。 请你帮她从`exam_record`数据表中计算所有用户完成 SQL 类别高难度试卷得分的截断平均值(去掉一个最大值和一个最小值后的平均值)。 @@ -52,7 +52,7 @@ tag: 输入数据中至少有 3 个有效分数 -**思路一:**要找出高难度 sql 试卷,肯定需要联 examination_info 这张表,然后找出高难度的课程,由 examination_info 得知,高难度 sql 的 exam_id 为 9001,那么等下就以 exam_id = 9001 作为条件去查询; +**思路一:** 要找出高难度 sql 试卷,肯定需要联 examination_info 这张表,然后找出高难度的课程,由 examination_info 得知,高难度 sql 的 exam_id 为 9001,那么等下就以 exam_id = 9001 作为条件去查询; 先找出 9001 号考试 `select * from exam_record where exam_id = 9001` @@ -220,7 +220,7 @@ WHERE info.exam_id = record.exam_id 解释:表示截止当前,有 11 次试卷作答记录,已完成的作答次数为 7 次(中途退出的为未完成状态,其交卷时间和份数为 NULL),已完成的试卷有 9001 和 9002 两份。 -**思路:**这题一看到统计次数,肯定第一时间就要想到用`COUNT`这个函数来解决,问题是要统计不同的记录,该怎么来写?使用子查询就能解决这个题目(这题用 case when 也能写出来,解法类似,逻辑不同而已);首先在做这个题之前,让我们先来了解一下`COUNT`的基本用法; +**思路**: 这题一看到统计次数,肯定第一时间就要想到用`COUNT`这个函数来解决,问题是要统计不同的记录,该怎么来写?使用子查询就能解决这个题目(这题用 case when 也能写出来,解法类似,逻辑不同而已);首先在做这个题之前,让我们先来了解一下`COUNT`的基本用法; `COUNT()` 函数的基本语法如下所示: @@ -264,7 +264,7 @@ SELECT COUNT(DISTINCT column_name1, column_name2) FROM table_name; 另外,`COUNT()` 函数的结果是一个整数值。即使结果是零,也不会返回 NULL,这点需要谨记。 -**答案:** +**答案**: ```sql SELECT @@ -283,7 +283,7 @@ FROM ### 得分不小于平均分的最低分 -**描述:**请从试卷作答记录表中找到 SQL 试卷得分不小于该类试卷平均得分的用户最低得分。 +**描述**: 请从试卷作答记录表中找到 SQL 试卷得分不小于该类试卷平均得分的用户最低得分。 示例数据 exam_record 表(uid 用户 ID, exam_id 试卷 ID, start_time 开始作答时间, submit_time 交卷时间, score 得分): @@ -315,7 +315,7 @@ FROM **解释**:试卷 9001 和 9002 为 SQL 类别,作答这两份试卷的得分有[80,89,87,90],平均分为 86.5,不小于平均分的最小分数为 87 -**思路:**这类题目第一眼看确实很复杂, 因为不知道从哪入手,但是当我们仔细读题审题后,要学会抓住题干中的关键信息。以本题为例:`请从试卷作答记录表中找到SQL试卷得分不小于该类试卷平均得分的用户最低得分。`你能一眼从中提取哪些有效信息来作为解题思路? +**思路**:这类题目第一眼看确实很复杂, 因为不知道从哪入手,但是当我们仔细读题审题后,要学会抓住题干中的关键信息。以本题为例:`请从试卷作答记录表中找到SQL试卷得分不小于该类试卷平均得分的用户最低得分。`你能一眼从中提取哪些有效信息来作为解题思路? 第一条:找到==SQL==试卷得分 @@ -337,7 +337,7 @@ select ROUND(AVG(score), 1) from examination_info info INNER JOIN exam_record 然后再找出该类试卷的最低得分,接着将结果集`【80, 89,87,90】` 去和平均分数作比较,方可得出最终答案。 -**答案:** +**答案**: ```sql SELECT MIN(score) AS min_score_over_avg @@ -359,7 +359,7 @@ WHERE info.exam_id = record.exam_id ### 平均活跃天数和月活人数 -**描述:**用户在牛客试卷作答区作答记录存储在表 `exam_record` 中,内容如下: +**描述**:用户在牛客试卷作答区作答记录存储在表 `exam_record` 中,内容如下: `exam_record` 表(`uid` 用户 ID, `exam_id` 试卷 ID, `start_time` 开始作答时间, `submit_time` 交卷时间, `score` 得分) @@ -390,7 +390,7 @@ WHERE info.exam_id = record.exam_id 注:此处活跃指有==交卷==行为。 -**思路:**读完题先注意高亮部分;一般求天数和月活跃人数马上就要想到相关的日期函数;这一题我们同样来进行拆分,把问题细化再解决;首先求活跃人数,肯定要用到`COUNT()`,那这里首先就有一个坑,不知道大家注意了没有?用户 1002 在 9 月份做了两种不同的试卷,所以这里要注意去重,不然在统计的时候,活跃人数是错的;第二个就是要知道日期的格式化,如上表,题目要求以`202107`这种日期格式展现,要用到`DATE_FORMAT`来进行格式化。 +**思路**:读完题先注意高亮部分;一般求天数和月活跃人数马上就要想到相关的日期函数;这一题我们同样来进行拆分,把问题细化再解决;首先求活跃人数,肯定要用到`COUNT()`,那这里首先就有一个坑,不知道大家注意了没有?用户 1002 在 9 月份做了两种不同的试卷,所以这里要注意去重,不然在统计的时候,活跃人数是错的;第二个就是要知道日期的格式化,如上表,题目要求以`202107`这种日期格式展现,要用到`DATE_FORMAT`来进行格式化。 基本用法: @@ -399,7 +399,7 @@ WHERE info.exam_id = record.exam_id - `date_value` 参数是待格式化的日期或时间值。 - `format` 参数是指定的日期或时间格式(这个和 Java 里面的日期格式一样)。 -**答案:** +**答案**: ```sql SELECT DATE_FORMAT(submit_time, '%Y%m') MONTH, @@ -414,7 +414,7 @@ GROUP BY MONTH ### 月总刷题数和日均刷题数 -**描述:**现有一张题目练习记录表 `practice_record`,示例内容如下: +**描述**:现有一张题目练习记录表 `practice_record`,示例内容如下: | id | uid | question_id | submit_time | score | | ---- | ---- | ----------- | ------------------- | ----- | @@ -471,7 +471,7 @@ SELECT DAY(LAST_DAY(NOW())) AS days_in_current_month; 有了上述的分析之后,即可马上写出答案,这题复杂就复杂在处理日期上,其中的逻辑并不难。 -**答案:** +**答案**: ```sql SELECT DATE_FORMAT(submit_time, '%Y%m') submit_month, @@ -493,9 +493,7 @@ ORDER BY submit_month ### 未完成试卷数大于 1 的有效用户(较难) -**描述** - -现有试卷作答记录表 `exam_record`(`uid` 用户 ID, `exam_id` 试卷 ID, `start_time` 开始作答时间, `submit_time` 交卷时间, `score` 得分),示例数据如下: +**描述**:现有试卷作答记录表 `exam_record`(`uid` 用户 ID, `exam_id` 试卷 ID, `start_time` 开始作答时间, `submit_time` 交卷时间, `score` 得分),示例数据如下: | id | uid | exam_id | start_time | submit_time | score | | ---- | ---- | ------- | ------------------- | ------------------- | ------ | @@ -561,7 +559,7 @@ GROUP_CONCAT([DISTINCT] expr [ORDER BY {unsigned_integer | col_name | expr} [ASC `GROUP_CONCAT()` 函数常用于 `GROUP BY` 子句中,将一组行的值连接为一个字符串,并在结果集中以聚合的形式返回。 -**答案:** +**答案**: ```sql SELECT a.uid, @@ -591,7 +589,7 @@ ORDER BY incomplete_cnt DESC ### 月均完成试卷数不小于 3 的用户爱作答的类别(较难) -**描述:**现有试卷作答记录表 `exam_record`(`uid`:用户 ID, `exam_id`:试卷 ID, `start_time`:开始作答时间, `submit_time`:交卷时间,没提交的话为 NULL, `score`:得分),示例数据如下: +**描述**:现有试卷作答记录表 `exam_record`(`uid`:用户 ID, `exam_id`:试卷 ID, `start_time`:开始作答时间, `submit_time`:交卷时间,没提交的话为 NULL, `score`:得分),示例数据如下: | id | uid | exam_id | start_time | submit_time | score | | ---- | ---- | ------- | ------------------- | ------------------- | ------ | @@ -627,7 +625,7 @@ ORDER BY incomplete_cnt DESC **解释**:用户 1002 和 1005 在 2021 年 09 月的完成试卷数目均为 3,其他用户均小于 3;然后用户 1002 和 1005 作答过的试卷 tag 分布结果按作答次数降序排序依次为 C++、SQL、算法。 -**思路:**这题考察联合子查询,重点在于`月均回答>=3`, 但是个人认为这里没有表述清楚,应该直接说查 9 月的就容易理解多了;这里不是每个月都要>=3 或者是所有答题次数/答题月份。不要理解错误了。 +**思路**:这题考察联合子查询,重点在于`月均回答>=3`, 但是个人认为这里没有表述清楚,应该直接说查 9 月的就容易理解多了;这里不是每个月都要>=3 或者是所有答题次数/答题月份。不要理解错误了。 先查询出哪些用户月均答题大于三次 @@ -658,7 +656,7 @@ ORDER BY tag_cnt DESC ### 试卷发布当天作答人数和平均分 -**描述:**现有用户信息表 `user_info`(`uid` 用户 ID,`nick_name` 昵称, `achievement` 成就值, `level` 等级, `job` 职业方向, `register_time` 注册时间),示例数据如下: +**描述**:现有用户信息表 `user_info`(`uid` 用户 ID,`nick_name` 昵称, `achievement` 成就值, `level` 等级, `job` 职业方向, `register_time` 注册时间),示例数据如下: | id | uid | nick_name | achievement | level | job | register_time | | ---- | ---- | --------- | ----------- | ----- | ---- | ------------------- | @@ -708,7 +706,7 @@ ORDER BY tag_cnt DESC 解释:只有一张 SQL 类别的试卷,试卷 ID 为 9001,发布当天(2021-09-01)有 1001、1002、1003、1005 作答过,但是 1003 是 5 级用户,其他 3 位为 5 级以上,他们三的得分有[70,80,85,90],平均分为 81.3(保留 1 位小数)。 -**思路:**这题看似很复杂,但是先逐步将“外边”条件拆分,然后合拢到一起,答案就出来,多表查询反正记住:由外向里,抽丝剥茧。 +**思路**:这题看似很复杂,但是先逐步将“外边”条件拆分,然后合拢到一起,答案就出来,多表查询反正记住:由外向里,抽丝剥茧。 先把三种表连起来,同时给定一些条件,比如题目中要求`等级> 5`的用户,那么可以先查出来 @@ -726,7 +724,7 @@ WHERE e_info.exam_id = record.exam_id 对试卷发布日期和开始考试日期进行比较:`DATE(e_info.release_time) = DATE(record.start_time)`;不用担心`submit_time` 为 null 的问题,后续在 where 中会给过滤掉。 -**答案:** +**答案**: ```sql SELECT record.exam_id AS exam_id, @@ -750,7 +748,7 @@ ORDER BY uv DESC, ### 作答试卷得分大于过 80 的人的用户等级分布 -**描述:** +**描述**: 现有用户信息表 `user_info`(`uid` 用户 ID,`nick_name` 昵称, `achievement` 成就值, `level` 等级, `job` 职业方向, `register_time` 注册时间): @@ -803,7 +801,7 @@ ORDER BY uv DESC, **思路:**这题和上一题都是一样的数据,只是查询条件改变了而已,上一题理解了,这题分分钟做出来。 -**答案:** +**答案**: ```sql SELECT u_info.LEVEL AS LEVEL, @@ -824,7 +822,7 @@ ORDER BY level_cnt DESC ### 每个题目和每份试卷被作答的人数和次数 -**描述:** +**描述**: 现有试卷作答记录表 exam_record(uid 用户 ID, exam_id 试卷 ID, start_time 开始作答时间, submit_time 交卷时间, score 得分): @@ -890,7 +888,7 @@ ORDER BY uv DESC, 还有一种`order by`不起作用的情况,但是能在子句的子句中起作用,这里的解决方案就是在外面再套一层查询。 -**答案:** +**答案**: ```sql SELECT * @@ -914,9 +912,7 @@ FROM ### 分别满足两个活动的人 -**描述:** - -为了促进更多用户在牛客平台学习和刷题进步,我们会经常给一些既活跃又表现不错的用户发放福利。假使以前我们有两拨运营活动,分别给每次试卷得分都能到 85 分的人(activity1)、至少有一次用了一半时间就完成高难度试卷且分数大于 80 的人(activity2)发了福利券。 +**描述**: 为了促进更多用户在牛客平台学习和刷题进步,我们会经常给一些既活跃又表现不错的用户发放福利。假使以前我们有两拨运营活动,分别给每次试卷得分都能到 85 分的人(activity1)、至少有一次用了一半时间就完成高难度试卷且分数大于 80 的人(activity2)发了福利券。 现在,需要你一次性将这两个活动满足的人筛选出来,交给运营同学。请写出一个 SQL 实现:输出 2021 年里,所有每次试卷得分都能到 85 分的人以及至少有一次用了一半时间就完成高难度试卷且分数大于 80 的人的 id 和活动号,按用户 ID 排序输出。 @@ -947,11 +943,9 @@ FROM | 1004 | activity1 | | 1004 | activity2 | -**解释:**用户 1001 最小分数 81 不满足活动 1,但 29 分 59 秒完成了 60 分钟长的试卷得分 81,满足活动 2;1003 最小分数 86 满足活动 1,完成时长都大于试卷时长的一半,不满足活动 2;用户 1004 刚好用了一半时间(30 分钟整)完成了试卷得分 85,满足活动 1 和活动 2。 +**解释**:用户 1001 最小分数 81 不满足活动 1,但 29 分 59 秒完成了 60 分钟长的试卷得分 81,满足活动 2;1003 最小分数 86 满足活动 1,完成时长都大于试卷时长的一半,不满足活动 2;用户 1004 刚好用了一半时间(30 分钟整)完成了试卷得分 85,满足活动 1 和活动 2。 -**思路:** - -这一题需要涉及到时间的减法,需要用到 `TIMESTAMPDIFF()` 函数计算两个时间戳之间的分钟差值。 +**思路**: 这一题需要涉及到时间的减法,需要用到 `TIMESTAMPDIFF()` 函数计算两个时间戳之间的分钟差值。 下面我们来看一下基本用法 @@ -986,7 +980,7 @@ WHERE info.exam_id = record.exam_id 然后再把两者`UNION` 起来即可。(这里特别要注意括号问题和`order by`位置,具体用法在上一篇中已提及) -**答案:** +**答案**: ```sql SELECT DISTINCT UID UID, @@ -1013,7 +1007,7 @@ ORDER BY UID ### 满足条件的用户的试卷完成数和题目练习数(困难) -**描述:** +**描述**: 现有用户信息表 user_info(uid 用户 ID,nick_name 昵称, achievement 成就值, level 等级, job 职业方向, register_time 注册时间): @@ -1099,7 +1093,7 @@ WHERE 第二,必须是`COUNT(distinct er.exam_id) exam_cnt, COUNT(distinct pr.id) question_cnt,`要加 distinct,因为有左连接产生很多重复值。 -**答案:** +**答案**: ```sql SELECT er.uid AS UID, @@ -1128,7 +1122,7 @@ ORDER BY exam_cnt, ### 每个 6/7 级用户活跃情况(困难) -**描述:** +**描述**: 现有用户信息表 `user_info`(`uid` 用户 ID,`nick_name` 昵称, `achievement` 成就值, `level` 等级, `job` 职业方向, `register_time` 注册时间): @@ -1265,7 +1259,7 @@ SELECT 最后将两个结果进行`UNION` 最后别忘了将结果进行排序 (这题有点类似于分治法的思想) -**答案:** +**答案**: ```sql SELECT user_info.uid, diff --git a/docs/database/sql/sql-questions-04.md b/docs/database/sql/sql-questions-04.md index 4e839eeb..45ab6072 100644 --- a/docs/database/sql/sql-questions-04.md +++ b/docs/database/sql/sql-questions-04.md @@ -69,7 +69,7 @@ FROM table; ### 每类试卷得分前三名 -**描述:** +**描述**: 现有试卷信息表 `examination_info`(`exam_id` 试卷 ID, `tag` 试卷类别, `difficulty` 试卷难度, `duration` 考试时长, `release_time` 发布时间): @@ -105,9 +105,9 @@ FROM table; | 算法 | 1006 | 2 | | 算法 | 1003 | 3 | -**解释:**有作答得分记录的试卷 tag 有 SQL 和算法,SQL 试卷用户 1001、1002、1003、1004 有作答得分,最高得分分别为 81、81、89、85,最低得分分别为 78、81、86、40,因此先按最高得分排名再按最低得分排名取前三为 1003、1004、1002。 +**解释**:有作答得分记录的试卷 tag 有 SQL 和算法,SQL 试卷用户 1001、1002、1003、1004 有作答得分,最高得分分别为 81、81、89、85,最低得分分别为 78、81、86、40,因此先按最高得分排名再按最低得分排名取前三为 1003、1004、1002。 -**答案:** +**答案**: ```sql SELECT tag, @@ -130,7 +130,7 @@ WHERE ranking <= 3 ### 第二快/慢用时之差大于试卷时长一半的试卷(较难) -**描述:** +**描述**: 现有试卷信息表 `examination_info`(`exam_id` 试卷 ID, `tag` 试卷类别, `difficulty` 试卷难度, `duration` 考试时长, `release_time` 发布时间): @@ -171,7 +171,7 @@ WHERE ranking <= 3 第二步,与通过试卷信息表 b 建立内连接,并根据试卷 id 分组,利用`having`筛选排名为第二个数据,将秒转化为分钟并进行比较,最后再根据试卷 id 倒序排序就行 -**答案:** +**答案**: ```sql SELECT a.exam_id, @@ -212,7 +212,7 @@ ORDER BY a.exam_id DESC | ---- | ----------- | ------------ | | 1006 | 6 | 2.57 | -**解释:**用户 1006 分别在 20210901、20210906、20210907 作答过 3 次试卷,连续两次作答最大时间窗为 6 天(1 号到 6 号),他 1 号到 7 号这 7 天里共做了 3 张试卷,平均每天 3/7=0.428571 张,那么 6 天里平均会做 0.428571\*6=2.57 张试卷(保留两位小数);用户 1005 在 20210905 做了两张试卷,但是只有一天的作答记录,过滤掉。 +**解释**:用户 1006 分别在 20210901、20210906、20210907 作答过 3 次试卷,连续两次作答最大时间窗为 6 天(1 号到 6 号),他 1 号到 7 号这 7 天里共做了 3 张试卷,平均每天 3/7=0.428571 张,那么 6 天里平均会做 0.428571\*6=2.57 张试卷(保留两位小数);用户 1005 在 20210905 做了两张试卷,但是只有一天的作答记录,过滤掉。 **思路:** @@ -220,7 +220,7 @@ ORDER BY a.exam_id DESC 而且要注意时间差要+1 天;还要注意==没交卷也算在内==!!!! (反正感觉这题描述不清,出的不是很好) -**答案:** +**答案**: ```sql SELECT UID, @@ -241,7 +241,7 @@ ORDER BY days_window DESC, ### 近三个月未完成为 0 的用户完成情况 -**描述:** +**描述**: 现有试卷作答记录表 `exam_record`(`uid`:用户 ID, `exam_id`:试卷 ID, `start_time`:开始作答时间, `submit_time`:交卷时间,为空的话则代表未完成, `score`:得分): @@ -264,7 +264,7 @@ ORDER BY days_window DESC, | ---- | ----------------- | | 1006 | 3 | -**解释:**用户 1006 近三个有作答试卷的月份为 202109、202108、202106,作答试卷数为 3,全部完成;用户 1001 近三个有作答试卷的月份为 202109、202108、202107,作答试卷数为 5,完成试卷数为 4,因为有未完成试卷,故过滤掉。 +**解释**:用户 1006 近三个有作答试卷的月份为 202109、202108、202106,作答试卷数为 3,全部完成;用户 1001 近三个有作答试卷的月份为 202109、202108、202107,作答试卷数为 5,完成试卷数为 4,因为有未完成试卷,故过滤掉。 **思路:** @@ -274,7 +274,7 @@ ORDER BY days_window DESC, 4. 拼装剩余条件 5. 排序 -**答案:** +**答案**: ```sql SELECT UID, @@ -292,7 +292,7 @@ ORDER BY exam_complete_cnt DESC, ### 未完成率较高的 50%用户近三个月答卷情况(困难) -**描述:** +**描述**: 现有用户信息表 `user_info`(`uid` 用户 ID,`nick_name` 昵称, `achievement` 成就值, `level` 等级, `job` 职业方向, `register_time` 注册时间): @@ -392,7 +392,7 @@ LEFT JOIN user_info USING (UID) WHERE LEVEL IN (6,7) ``` -**答案:** +**答案**: ```sql SELECT t1.uid, @@ -430,7 +430,7 @@ ORDER BY t1.uid, ### 试卷完成数同比 2020 年的增长率及排名变化(困难) -**描述:** +**描述**: 现有试卷信息表 `examination_info`(`exam_id` 试卷 ID, `tag` 试卷类别, `difficulty` 试卷难度, `duration` 考试时长, `release_time` 发布时间): @@ -507,7 +507,7 @@ SELECT CAST('123' AS INT); 示例就不一一举例了,这个函数很简单 -**答案:** +**答案**: ```sql SELECT @@ -586,7 +586,7 @@ ORDER BY ### 对试卷得分做 min-max 归一化 -**描述:** +**描述**: 现有试卷信息表 `examination_info`(`exam_id` 试卷 ID, `tag` 试卷类别, `difficulty` 试卷难度, `duration` 考试时长, `release_time` 发布时间): @@ -647,7 +647,7 @@ ORDER BY 最后就是仔细看上面公式 (说实话,这题看起来就很绕) -**答案:** +**答案**: ```sql SELECT @@ -730,7 +730,7 @@ ORDER BY 这个是关键`**sum(count(*)) over(partition by exam_id order by date_format(start_time,'%Y%m'))**` -**答案:** +**答案**: ```sql SELECT exam_id, @@ -745,7 +745,7 @@ GROUP BY exam_id, ### 每月及截止当月的答题情况(较难) -**描述:**现有试卷作答记录表 `exam_record`(`uid` 用户 ID, `exam_id` 试卷 ID, `start_time` 开始作答时间, `submit_time` 交卷时间, `score` 得分): +**描述**:现有试卷作答记录表 `exam_record`(`uid` 用户 ID, `exam_id` 试卷 ID, `start_time` 开始作答时间, `submit_time` 交卷时间, `score` 得分): | id | uid | exam_id | start_time | submit_time | score | | ---- | ---- | ------- | ------------------- | ------------------- | ------ | @@ -800,7 +800,7 @@ GROUP BY exam_id, (3)统计截止当月的单月最大新增用户数、截止当月的累积用户数 ,最终按照按月份升序输出 -**答案:** +**答案**: ```sql -- 截止当月的单月最大新增用户数、截止当月的累积用户数,按月份升序输出 diff --git a/docs/database/sql/sql-questions-05.md b/docs/database/sql/sql-questions-05.md index 371b2183..7a527ef3 100644 --- a/docs/database/sql/sql-questions-05.md +++ b/docs/database/sql/sql-questions-05.md @@ -14,7 +14,7 @@ tag: ### 统计有未完成状态的试卷的未完成数和未完成率 -**描述:** +**描述**: 现有试卷作答记录表 `exam_record`(`uid` 用户 ID, `exam_id` 试卷 ID, `start_time` 开始作答时间, `submit_time` 交卷时间, `score` 得分),数据如下: @@ -32,11 +32,11 @@ tag: 解释:试卷 9001 有 3 次被作答的记录,其中两次完成,1 次未完成,因此未完成数为 1,未完成率为 0.333(保留 3 位小数) -**思路:** +**思路**: 这题只需要注意一个是有条件限制,一个是没条件限制的;要么分别查询条件,然后合并;要么直接在 select 里面进行条件判断。 -**答案:** +**答案**: 写法 1: @@ -64,7 +64,7 @@ HAVING incomplete_cnt <> 0 ### 0 级用户高难度试卷的平均用时和平均得分 -**描述:** +**描述**: 现有用户信息表 `user_info`(`uid` 用户 ID,`nick_name` 昵称, `achievement` 成就值, `level` 等级, `job` 职业方向, `register_time` 注册时间),数据如下: @@ -101,9 +101,9 @@ HAVING incomplete_cnt <> 0 解释:0 级用户有 1001,高难度试卷有 9001,1001 作答 9001 的记录有 3 条,分别用时 20 分钟、未完成(试卷时长 60 分钟)、30 分钟(未满 31 分钟),分别得分为 80 分、未完成(0 分处理)、20 分。因此他的平均用时为 110/3=36.7(保留一位小数),平均得分为 33 分(取整) -**思路:**这题用`IF`是判断的最方便的,因为涉及到 NULL 值的判断。当然 `case when`也可以,大同小异。这题的难点就在于空值的处理,其他的这些查询条件什么的,我相信难不倒大家。 +**思路**:这题用`IF`是判断的最方便的,因为涉及到 NULL 值的判断。当然 `case when`也可以,大同小异。这题的难点就在于空值的处理,其他的这些查询条件什么的,我相信难不倒大家。 -**答案:** +**答案**: ```sql SELECT UID, @@ -125,7 +125,7 @@ ORDER BY UID ### 筛选限定昵称成就值活跃日期的用户(较难) -**描述:** +**描述**: 现有用户信息表 `user_info`(`uid` 用户 ID,`nick_name` 昵称, `achievement` 成就值, `level` 等级, `job` 职业方向, `register_time` 注册时间): @@ -184,7 +184,7 @@ ORDER BY UID 因此最终满足条件的只有 1002。 -**思路:** +**思路**: 先根据条件列出主要查询语句 @@ -194,7 +194,7 @@ ORDER BY UID 第三个条件因为限定了为 9 月,所以直接写就行:`( date_format( record.submit_time, '%Y%m' )= 202109 OR date_format( pr.submit_time, '%Y%m' )= 202109 )` -**答案:** +**答案**: ```sql SELECT DISTINCT u_info.uid, @@ -213,7 +213,7 @@ GROUP BY u_info.uid ### 筛选昵称规则和试卷规则的作答记录(较难) -**描述:** +**描述**: 现有用户信息表 `user_info`(`uid` 用户 ID,`nick_name` 昵称, `achievement` 成就值, `level` 等级, `job` 职业方向, `register_time` 注册时间): @@ -274,7 +274,7 @@ c 开头的试卷有 9001、9002; 1002 完成 9002 的得分有 90、82、83,平均分为 85; -**思路:** +**思路**: 还是老样子,既然给出了条件,就先把各个条件先写出来 @@ -284,7 +284,7 @@ c 开头的试卷有 9001、9002; 对于字母 c 开头的试卷类别: `e_info.tag LIKE 'c%'` 或者 `tag regexp '^c|^C'` 第一个也能匹配到大写 C -**答案:** +**答案**: ```sql SELECT UID, @@ -307,7 +307,7 @@ ORDER BY UID,avg_score; ### 根据指定记录是否存在输出不同情况(困难) -**描述:** +**描述**: 现有用户信息表 `user_info`(`uid` 用户 ID,`nick_name` 昵称, `achievement` 成就值, `level` 等级, `job` 职业方向, `register_time` 注册时间): @@ -356,7 +356,7 @@ ORDER BY UID,avg_score; 附:如果 1001 不满足『未完成试卷数大于 2』,则需要输出 1001、1002、1003 的这两个指标,因为试卷作答记录表里只有这三个用户的作答记录。 -**思路:** +**思路**: 先把可能满足条件**“0 级用户未完成试卷数大于 2”**的 SQL 写出来 @@ -533,7 +533,7 @@ CASE ### 各用户等级的不同得分表现占比(较难) -**描述:** +**描述**: 现有用户信息表 `user_info`(`uid` 用户 ID,`nick_name` 昵称, `achievement` 成就值, `level` 等级, `job` 职业方向, `register_time` 注册时间): @@ -591,7 +591,7 @@ CASE 因此 0 级用户(只有 1001)的各分数等级比例为:优 1/6,良 1/6,中 1/6,差 3/6;3 级用户(只有 1002)各分数等级比例为:优 1/3,良 2/3。结果保留 3 位小数。 -**思路:** +**思路**: 先把 **“将试卷得分按分界点[90,75,60]分为优良中差四个得分等级”**这个条件写出来,这里可以用到`case when` @@ -608,7 +608,7 @@ END 这题的关键点就在于这,其他剩下的就是条件拼接了 -**答案:** +**答案**: ```sql SELECT a.LEVEL, @@ -645,7 +645,7 @@ ORDER BY a.LEVEL DESC, ### 注册时间最早的三个人 -**描述:** +**描述**: 现有用户信息表 `user_info`(`uid` 用户 ID,`nick_name` 昵称, `achievement` 成就值, `level` 等级, `job` 职业方向, `register_time` 注册时间): @@ -668,7 +668,7 @@ ORDER BY a.LEVEL DESC, 解释:按注册时间排序后选取前三名,输出其用户 ID、昵称、注册时间。 -**答案:** +**答案**: ```sql SELECT uid, nick_name, register_time @@ -679,7 +679,7 @@ SELECT uid, nick_name, register_time ### 注册当天就完成了试卷的名单第三页(较难) -**描述:**现有用户信息表 `user_info`(`uid` 用户 ID,`nick_name` 昵称, `achievement` 成就值, `level` 等级, `job` 职业方向, `register_time` 注册时间): +**描述**:现有用户信息表 `user_info`(`uid` 用户 ID,`nick_name` 昵称, `achievement` 成就值, `level` 等级, `job` 职业方向, `register_time` 注册时间): | id | uid | nick_name | achievement | level | job | register_time | | ---- | ---- | ----------- | ----------- | ----- | ---- | ------------------- | @@ -752,13 +752,13 @@ SELECT uid, nick_name, register_time 每页 3 条,第三页也就是第 7~9 条,返回 1010、1003、1004 的行记录即可。 -**思路:** +**思路**: 1. 每页三条,即需要取出第三页的人的信息,要用到`limit` 2. 统计求职方向为算法工程师且注册当天就完成了算法类试卷的人的**信息和每次记录的得分**,先求满足条件的用户,后用 left join 做连接查找信息和每次记录的得分 -**答案:** +**答案**: ```sql SELECT t1.uid, @@ -782,7 +782,7 @@ LIMIT 6,3 ### 修复串列了的记录 -**描述:**现有试卷信息表 `examination_info`(`exam_id` 试卷 ID, `tag` 试卷类别, `difficulty` 试卷难度, `duration` 考试时长, `release_time` 发布时间): +**描述**:现有试卷信息表 `examination_info`(`exam_id` 试卷 ID, `tag` 试卷类别, `difficulty` 试卷难度, `duration` 考试时长, `release_time` 发布时间): | id | exam_id | tag | difficulty | duration | release_time | | ---- | ------- | -------------- | ---------- | -------- | ------------------- | @@ -799,7 +799,7 @@ LIMIT 6,3 | ------- | ---- | ---------- | -------- | | 9004 | 算法 | medium | 80 | -**思路:** +**思路**: 先来学习下本题要用到的函数 @@ -847,7 +847,7 @@ SUBSTRING_INDEX(str, delimiter, count) -- 输出结果:'banana,cherry' ``` -**答案:** +**答案**: ```sql SELECT @@ -863,7 +863,7 @@ WHERE ### 对过长的昵称截取处理 -**描述:**现有用户信息表 `user_info`(`uid` 用户 ID,`nick_name` 昵称, `achievement` 成就值, `level` 等级, `job` 职业方向, `register_time` 注册时间): +**描述**:现有用户信息表 `user_info`(`uid` 用户 ID,`nick_name` 昵称, `achievement` 成就值, `level` 等级, `job` 职业方向, `register_time` 注册时间): | id | uid | nick_name | achievement | level | job | register_time | | ---- | ---- | ---------------------- | ----------- | ----- | ---- | ------------------- | @@ -885,7 +885,7 @@ WHERE 解释:字符数大于 10 的用户有 1005 和 1006,长度分别为 13、17;因此需要对 1006 的昵称截断输出。 -**思路:** +**思路**: 这题涉及到字符的计算,要计算字符串的字符数(即字符串的长度),可以使用 `LENGTH` 函数或 `CHAR_LENGTH` 函数。这两个函数的区别在于对待多字节字符的方式。 @@ -905,7 +905,7 @@ SELECT LENGTH('你好'); -- 输出结果:6,因为 '你好' 中的每个汉 SELECT CHAR_LENGTH('你好'); -- 输出结果:2,因为 '你好' 中有两个字符,即两个汉字 ``` -**答案:** +**答案**: ```sql SELECT @@ -925,7 +925,7 @@ GROUP BY ### 大小写混乱时的筛选统计(较难) -**描述:** +**描述**: 现有试卷信息表 `examination_info`(`exam_id` 试卷 ID, `tag` 试卷类别, `difficulty` 试卷难度, `duration` 考试时长, `release_time` 发布时间): @@ -981,7 +981,7 @@ GROUP BY 作答次数小于 3 的 tag 有 c++和 sql,而转为大写后只有 C++本来就有作答数,于是输出 c++转化大写后的作答次数为 6。 -**思路:** +**思路**: 首先,这题有点混乱,9004 根据示例数据查出来只有 1 次,这里显示有 2 次。 @@ -993,7 +993,7 @@ GROUP BY 难点在于相同表做连接要查询不同的值 -**答案:** +**答案**: ```sql WITH a AS