• 【SQL】下|牛客网SQL非技术入门40道代码|练习记录


     跟着刷题:22 统计每个学校的答过题的用户的平均答题数_哔哩哔哩_bilibili

    前面纯纯纯是语法开始,第22题开始就要仔细分析业务了......

     22统计每个学校的答过题的用户的平均题数

    说明:某学校用户平均答题数量计算方式为该学校用户答题总次数除以答过题的不同用户个数

    1. select
    2. b.university as university
    3. , count(1) / count(distinct a.device_id) as avg_answer_cnt
    4. from question_practice_detail a
    5. left join user_profile b
    6. on a.device_id = b.device_id
    7. group by 1
    8. order by 1

    23统计每个学校各难度的用户平均刷题数(这题有详细解题步骤,因为开始变难了)

    事实表、维度表、信息表?

    • 事实表:带着业务的?
    • 这里的用户信息表user_profile是一张维度表

    第一步连接(多表连接)

    1. select
    2. from question_practice_detail a
    3. left join user_profile b
    4. on a.device_id = b.device_id
    5. left join question_detail c
    6. on a.question_id = c.question_id

    第二步选出结果要的列

    第三步根据限定操作(不同学校、不同难度--->分组)

    bug?刚刚在牛客提交,明明是一样的答案提交错误,等了一会就可以了

    1. select
    2. b.university as university
    3. ,c.difficult_level as difficult_level
    4. ,count(1) / count(distinct a.device_id) as avg_answer_cnt
    5. from question_practice_detail a
    6. left join user_profile b
    7. on a.device_id = b.device_id
    8. left join question_detail c
    9. on a.question_id = c.question_id
    10. group by 1, 2

    24统计每个用户的平均刷题数

    24题与23题只有些许不同

    法1:全部连接之后再筛选

    此时,所有行都是有值的,然后筛选出“山东大学”的

    解释:

    1. JOIN操作:首先对question_practice_detail表(a)与user_profile表(b)进行左连接,然后将结果与question_detail表(c)进行左连接。这意味着所有question_practice_detail的记录都会被包含,即使它们在user_profilequestion_detail中没有匹配项。
    2. WHERE子句:在完成连接后,通过WHERE b.university = '山东大学'过滤出大学为“山东大学”的记录。这意味着只有当user_profile表中的设备ID对应的是山东大学的学生时,这些记录才会被计算在内。但是,因为这个条件放在了JOIN之后,它实际上转化成了对整个JOIN结果集的过滤,可能会影响到分母(即COUNT(DISTINCT a.device_id))的计算,因为它是在过滤之后计数的,理论上这不会影响到本例的结果,因为过滤条件是基于user_profile表的字段,但这种写法可能导致逻辑不清晰。
    3. GROUP BY:最后按照大学和题目难度级别分组,并计算每组的平均作答数量。
    1. select
    2. b.university as university
    3. ,c.difficult_level as difficult_level
    4. ,count(1) / count(distinct a.device_id) as avg_answer_cnt
    5. from question_practice_detail a
    6. left join user_profile b
    7. on a.device_id = b.device_id
    8. left join question_detail c
    9. on a.question_id = c.question_id
    10. where b.university = '山东大学'
    11. group by 1,2

    法2:也可以在连接的同时进行筛选

    或者说叫:筛选是山东大学的值连接,那么,左表会有一些值是其它大学的,因为右表没有值 给它连接,那么就连了空值

    1. 筛选条件的位置:筛选条件b.university = '山东大学'直接放在了JOIN语句中,而不是在WHERE子句里。这样做在逻辑上等同于先做了一个内连接的筛选,只包括了山东大学的记录,然后再进行其他操作。这种方式比在WHERE子句中应用筛选更加高效,因为它减少了需要处理的数据量,尤其是在user_profile表较大的情况下。
    2. 使用了WHERE b.device_id IS NOT NULL来进一步过滤掉那些在user_profile表中没有匹配到的设备ID记录。
    1. select
    2. b.university as university
    3. ,c.difficult_level as difficult_level
    4. ,count(1) / count(distinct a.device_id) as avg_answer_cnt
    5. from question_practice_detail a
    6. left join user_profile b
    7. on a.device_id = b.device_id
    8. and b.university = '山东大学'
    9. left join question_detail c
    10. on a.question_id = c.question_id
    11. where b.device_id is not null
    12. group by 1,2

    法3:法3相比前两个更好

    法3和法2很像,法3将left join 改为join:在连接的时候就会把右表没有对应值的行直接去掉

    连接类型的变化:这里,JOIN user_profile b 使用的是内连接(JOIN默认为内连接),而非之前的左连接(LEFT JOIN)。这意味着只有当question_practice_detail表中的设备ID在user_profile表中存在,并且该用户是山东大学的学生时,这些记录才会被包含在最终结果集中。这与前两个查询不同,前两者使用左连接保留了question_practice_detail表中的所有记录,即便在user_profile中找不到匹配项。

    1. select
    2. b.university as university
    3. ,c.difficult_level as difficult_level
    4. ,count(1) / count(distinct a.device_id) as avg_answer_cnt
    5. from question_practice_detail a
    6. join user_profile b
    7. on a.device_id = b.device_id
    8. and b.university = '山东大学'
    9. left join question_detail c
    10. on a.question_id = c.question_id
    11. group by 1,2

    25查找山东大学或者性别为男生的信息

    关键字:union,上下拼接(列的数量要匹配)

    • union:会帮你去重
    • union all:不去重

    第一步先分别完成以下逻辑:

    1. select
    2. device_id, gender, age, gpa
    3. from user_profile
    4. where university = '山东大学'
    5. select
    6. device_id, gender, age, gpa
    7. from user_profile
    8. where gender = 'male'

    第二步:用union关键字拼接:

    1. select
    2. device_id, gender, age, gpa
    3. from user_profile
    4. where university = '山东大学'
    5. union all
    6. select
    7. device_id, gender, age, gpa
    8. from user_profile
    9. where gender = 'male'

    26计算25岁以上和以下的用户数量

    用if函数,做辅助列?

    1. select
    2. if(age < 25 or age is null, '25岁以下', '25岁及以上') as age_cut
    3. ,count(1) as number
    4. from user_profile
    5. group by 1

    27查看不同年龄段的用户明细

    case when的使用

    当条件有三个或以上的时候就不用if了,改用case when

    case when 条件1 then

            when 条件2

            when 条件3

    else XX end

    1. select
    2. device_id
    3. ,gender
    4. ,case when age < 20 then '20岁以下'
    5. when age >= 20 and age <= 24 then '20-24岁'
    6. when age >= 25 then '25岁及以上'
    7. else '其他' end as age_cut
    8. from user_profile

    28计算用户8月每天的练题数量

    left()函数:字符串处理函数

    day()函数:可以取出日期中的天

    1. select
    2. day(date) as day
    3. ,count(1)
    4. from question_practice_detail
    5. where left(date, 7) = '2021-08'
    6. group by 1

    29计算用户的平均次日留存率

    临时表查询:with XX as()

    函数:date_add()   date_sub()

    自连接:找到下一天的日期,这里因为是要连接表,所以把下一天的日期减一天,让它跟上一天相等,才能连接表

    第一步:

    • 这里的WITH语句用于创建一个名为tmp的临时表。在tmp中,我们通过GROUP BY子句将每个device_id和对应的date分组,确保每个用户每天只有一条记录。
    • 然后,我们使用LEFT JOINtmp表连接到自身,通过a.device_id = b.device_ida.date = DATE_ADD(b.date, INTERVAL 1 DAY)来匹配用户在第二天的记录。如果用户在第二天有记录,b.device_id将不为NULL
    1. with tmp
    2. as(
    3. select
    4. device_id
    5. ,date
    6. from question_practice_detail
    7. group by 1,2
    8. )
    9. select
    10. *
    11. from tmp a
    12. left join tmp b
    13. on a.device_id = b.device_id
    14. and a.date = date_sub(b.date, interval 1 day)

    连接后的表长这样(部分截图):

    第二步:

    • 最后,我们使用SUMCASE语句来计算第二天做题的用户数,通过COUNT来计算第一天做题的用户总数,然后用第二天做题的用户数除以第一天的用户总数,得到平均留存率avg_ret
    • 注意谁是分子,谁是分母
    • 简言之,连接后的表有四列,我们只关注“前”“后”天之间的关系
    • 观察第一列和第二列看出是一个用户有刷题的所有日期的数据,然后所有用户数据上下拼接了。第三第四列就是是否存在后一天的数据,没有就是none
    • 前后天,第一天第二天,这里是说同一个东西
    1. with tmp
    2. as(
    3. select
    4. device_id
    5. ,date
    6. from question_practice_detail
    7. group by 1,2
    8. )
    9. select
    10. count(b.device_id) / count(a.device_id) as avg_ret
    11. from tmp a
    12. left join tmp b
    13. on a.device_id = b.device_id
    14. and a.date = date_sub(b.date, interval 1 day)

    30统计每种性别的人数

    substring_index()函数:取字段,分隔符

    • excel中可以分列做,在sql里没有分列
    • 一般很少用,可能是数据库里的数据一般都清洗好了吧

    1. select
    2. substring_index(profile, ',', -1) as gender
    3. ,count(1) as number
    4. from user_submit
    5. group by 1

    31提取博客URL中的用户名

    1. select
    2. device_id
    3. ,substring_index(blog_url, '/', -1) as gender
    4. from user_submit

    32截取出年龄

    如果不是最后一个字段的话,substring_index()就要写嵌套

    1. select
    2. substring_index(substring_index(profile, ',', 3), ',', -1) as age
    3. ,count(device_id) as number
    4. from user_submit
    5. group by 1

    33找出每个学校GPA最低的同学

    子查询的使用

    • 记得加别名

    如果一个学校有多个最低值--->使用窗口函数

    第一步:???

    1. select
    2. from(
    3. select
    4. university
    5. ,min(gpa) as gpa
    6. from user_profile
    7. group by 1
    8. ) a
    9. left join
    10. (
    11. select
    12. *
    13. from user_profile
    14. ) b
    15. on a.university = b.university
    16. and a.gpa = b.gpa

    第二步:选列,排序

    1. select
    2. b.device_id
    3. ,a.*
    4. from(
    5. select
    6. university
    7. ,min(gpa) as gpa
    8. from user_profile
    9. group by 1
    10. ) a
    11. left join
    12. (
    13. select
    14. *
    15. from user_profile
    16. ) b
    17. on a.university = b.university
    18. and a.gpa = b.gpa
    19. order by 2

    这33题有很多的解法,主要还是关注对表格结构(结果和原表)的分析和理解

    34统计复旦用户8月练题情况

    第一步:

    1. select
    2. from question_practice_detail a
    3. left join user_profile b
    4. on a.device_id = b.device_id
    5. where b.university = '复旦大学'
    6. and left(a.date, 7) = '2021-08'

    第二步:

    • sum(if(a.result = 'right', 1, 0))可以写成count(if(a.result = 'right', 1, null))

    1. select
    2. a.device_id as device_id
    3. ,b.university as university
    4. ,count(question_id) as question_cnt
    5. ,sum(if(a.result = 'right', 1, 0)) as right_question_cnt
    6. from question_practice_detail a
    7. left join user_profile b
    8. on a.device_id = b.device_id
    9. where b.university = '复旦大学'
    10. and left(a.date, 7) = '2021-08'
    11. group by 1

    第三步:

    1. select
    2. a.device_id as device_id
    3. ,b.university as university
    4. ,count(question_id) as question_cnt
    5. ,sum(if(a.result = 'right', 1, 0)) as right_question_cnt
    6. from question_practice_detail a
    7. left join user_profile b
    8. on a.device_id = b.device_id
    9. where b.university = '复旦大学'
    10. and left(a.date, 7) = '2021-08'
    11. group by 1
    12. union all
    13. select
    14. from user_profile a
    15. left join question_practice_detail b
    16. on a.device_id = b.device_id
    17. where b.device_id is null
    18. and a.university = '复旦大学'

    第四步:

    1. select
    2. a.device_id as device_id
    3. ,b.university as university
    4. ,count(question_id) as question_cnt
    5. ,sum(if(a.result = 'right', 1, 0)) as right_question_cnt
    6. from question_practice_detail a
    7. left join user_profile b
    8. on a.device_id = b.device_id
    9. where b.university = '复旦大学'
    10. and left(a.date, 7) = '2021-08'
    11. group by 1
    12. union all
    13. select
    14. a.device_id as device_id
    15. ,a.university as university
    16. ,0 as question_cnt
    17. ,0 as right_question_cnt
    18. from user_profile a
    19. left join question_practice_detail b
    20. on a.device_id = b.device_id
    21. where b.device_id is null
    22. and a.university = '复旦大学'

    35浙大不同难度题目的正确率

    36查找后排序

    37查找后多列排序

    38查找后降序排列

    39 21年8月份练题总数

  • 相关阅读:
    LeetCode刷题day23||669. 修剪二叉搜索树&&108.将有序数组转换为二叉搜索树&&538.把二叉搜索树转换为累加树--二叉树
    谨慎低调,一路通畅
    2022最新iOS最新打包发布流程
    某60区块链安全之Call函数簇滥用实战一学习记录
    js两数之和
    ES 架构及基础 - 1
    Golang-GJSON 快速而简单的方法来从 json 文档获取值
    node-sass安装失败的解决方法
    【软件工程导论】1.软件过程模型
    广东省科学技术厅关于2023年度广东省科学技术奖提名工作的通知
  • 原文地址:https://blog.csdn.net/m0_73972962/article/details/139806152