牛客题霸-SQL入门篇

173 阅读22分钟

你们好,我是金金金。

image.png

基础查询

简单处理查询结果

SQL3-查询结果去重

题目

image.png

答案

-- 使用 group by 分组语句
select university from user_profile group by university

-- 使用 distinct 关键词
select distinct university from user_profile

区别

  1. distinct:适合当你只需要知道某列中的不同值时。它很简单,但是功能有限,无法直接获取其他相关信息。
  2. group by:更适合当你需要基于某个字段进行分组,并希望同时获取该组内的其他信息或进行一些计算时。它的功能更强大,适用于更多样化的查询需求

思路

  1. 首先查找出所有记录的的学校字段数据
  2. 然后利用关键词进行分组/过滤即可

SQL4-查询结果限制返回行数

题目

image.png

答案

-- 使用 limit 关键词
select device_id from user_profile limit 2

select device_id from user_profile limit 0,2

思路

  1. 首先查出所有的数据
  2. 使用limit关键词进行限制返回条数即可

SQL5-将查询后的列重新命名

题目

image.png

答案

-- 使用 as 关键词 重命名
select device_id as user_infos_example from user_profile limit 2

思路

  1. 首先查找出所有数据 接着 利用limit关键词只返回前面两条数据
  2. 然后利用as关键词对列名进行重命名

条件查询

基础排序

SQL36-查找后排序

题目

image.png

答案

-- 使用 order by 排序语句(不写默认是ASC 升序,DESC则是降序)
select device_id, age from user_profile order by age ASC

思路

  1. 首先查出所有的device_idage字段
  2. 利用order byage进行排序即可

SQL37-查找后多列排序

题目

image.png

答案

-- 使用 order by 排序语句 可以设定多个字段排序
select device_id, gpa, age from user_profile order by gpa asc, age asc

思路

  1. 首先查出所有的device_idgpaage字段
  2. 利用order by对多个字段依次排序即可

SQL38-查找后降序排列

题目

image.png

答案

-- 使用 order by 即可
select device_id, gpa, age from user_profile order by gpa desc, age desc

思路

  1. 首先查出所有的device_idgpaage字段
  2. 利用order by对多个字段依次排序即可

注意desc不能省略,必须显性填写

基础操作符

SQL6-查找学校是北大的学生信息

题目

image.png

答案

-- 使用 `where` 子句进行筛选
select
    device_id,
    university
from
    user_profile
where
    university = '北京大学'

思路

  1. 首先查出device_iduniversity列数据
  2. 使用 where 子句进行筛选出university为北京大学的数据

SQL7-查找年龄大于24岁的用户信息

题目

image.png

答案

-- 使用 `where` 子句进行筛选
select
    device_id,
    gender,
    age,
    university
from
    user_profile
where
    age > 24

思路

  1. 首先查出device_idgenderageuniversity列数据
  2. 使用 where 子句筛选出age大于24的数据

SQL8-查找某个年龄段的用户信息

题目

image.png

答案

-- 使用 `where` 子句进行筛选,利用 `and` 关键字 做并且条件
select
    device_id,
    gender,
    age
from
    user_profile
where
    age >= 20 and age <= 23

思路

  1. 首先查出device_idgenderage列数据
  2. 使用 where 子句筛选,利用and关键字做并且条件筛选出大于等于20并且小于等于23年龄的行数据

SQL9-查找除复旦大学的用户信息

题目

image.png

答案

-- 使用 `where` 子句进行筛选,利用 `!=` 得到`university`不为复旦大学的数据
select
    device_id,
    gender,
    age,
    university
from 
    user_profile
where
    university != '复旦大学'

思路

  1. 首先查出device_idgenderageuniversity列数据
  2. 使用 != 得到university不为复旦大学的数据

SQL10-用where过滤空值练习

题目

image.png

答案

-- 使用 `where` 子句进行筛选,利用 `is not null`操作符筛出`age`不为`null`的数据
select
    device_id,
    gender,
    age,
    university
from
    user_profile
where
    age is not null

思路

  1. 首先查出device_idgenderageuniversity列数据
  2. 使用 is not null操作符得到age不为null的数据

高级操作符

SQL11-高级操作符练习(1)

题目

image.png

答案

-- 使用 `where` 子句进行筛选,利用 `and`连接运算符拼接并且条件
select
    device_id,
    gender,
    age,
    university,
    gpa
from
    user_profile
where
    gender = 'male' and gpa > 3.5

思路

  1. 首先查出device_idgenderageuniversitygpa列数据
  2. 使用 where子句过滤结果,利用and连接运算符实现条件的并且关系

SQL12-高级操作符练习(2)

题目

image.png

答案

-- 使用 `where` 子句进行筛选,利用 `or`连接运算符拼接或者条件
select
    device_id,
    gender,
    age,
    university,
    gpa
from
    user_profile
where
    university = '北京大学' or gpa > 3.7

思路

  1. 首先查出device_idgenderageuniversitygpa列数据
  2. 使用 where子句过滤结果,利用or连接运算符实现条件的或者关系

SQL14-操作符混合运用

题目

image.png

答案

-- 使用 `where` 子句进行筛选,利用 `and`及`or`运算符拼接条件,最后利用`order by`子句对 对应列排序即可
select
    device_id,
    gender,
    age,
    university,
    gpa
from
    user_profile
where
    (gpa > 3.5 and university = '山东大学')
    or 
    (gpa > 3.8 and university = '复旦大学')
order by
    device_id asc

思路

  1. 首先查出device_idgenderageuniversitygpa列数据
  2. 使用 where子句过滤结果,根据题目要求首先用and运算符拼接并且所要的条件(用括号包裹可读性更好)
  3. 使用or运算符拼接或者条件
  4. 最后用order by子句对对应列排序即可(asc可省略,默认就是升序排序)

SQL15-查看学校名称中含北京的用户

题目

image.png

答案

-- 使用 `where` 子句进行筛选,利用 `like`运算符匹配对应的值即可
select
    device_id,
    age,
    university
from
    user_profile
where
    university like '%北京%'

思路

  1. 首先查出device_idageuniversity列数据
  2. 使用 where子句过滤结果
  3. 使用like子句模糊查出university含有北京二字的数据即可

正则表达式

SQL40-电话号码格式校验

题目

image.png

答案

-- 使用 `where` 子句进行筛选,利用正则表达式当中的`REGEXP`操作符书写规则匹配对应的值
select
    *
from
    contacts
where
    phone_number REGEXP '^[1-9][0-9]{2}-?[0-9]{3}-?[0-9]{4}$'
order by
    id

思路

  1. 首先查出device_idageuniversity列数据
  2. 使用 where子句过滤结果
  3. 利用正则表达式当中的REGEXP操作符书写规则匹配对应的值

image.png

高级查询

计算函数

SQL16-查找GPA最高值

题目

image.png

答案

-- 使用 `where` 子句进行筛选出复旦大学的数据,利用`max()`函数返回指定列的最大值
select
    max(gpa) as gpa
from
    user_profile
where
    university = '复旦大学'

思路

  1. 使用 where子句过滤出复旦大学的数据
  2. 利用max()函数返回指定列的最大值并命名为gpa

SQL17-计算男生人数以及平均GPA

题目

image.png

答案

-- 使用 `where` 子句进行筛选出男生数据,利用`avg()`函数计算平均值,利用`count`函数返回匹配的行数
select
    count(id) as male_num,
    avg(gpa) as avg_gpa
from
    user_profile
where
    gender = 'male'

思路

  1. 使用 where子句过滤出男性用户
  2. 利用count()函数计数 获取匹配到的行数数量
  3. 利用avg()函数获取gpa列的平均值

分组查询

SQL18-分组计算练习题

题目

image.png

答案

-- 使用 `group by` 子句对性别和学校进行分组,利用`order by`子句排序,利用`count`函数返回匹配的行数计数,`round`函数用于把数值字段舍入为指定的小数位数
select
    gender,
    university,
    count(*) as user_num,
    round(avg(active_days_within_30), 1) as avg_active_day,
    round(avg(question_cnt), 1) as avg_question_cnt
from
    user_profile
group by
    gender,
    university
order by
    gender asc,
    university asc

思路

  1. 使用 group by子句对性别和学校分组
  2. 利用order by排序(其实也可以省略不写 默认就是升序排序)
  3. 利用count函数计数
  4. 利用round函数用于把数值字段舍入为指定的小数位数(第一个参数为舍入的字段(必须),第二个参数为返回的小数(可选))

SQL19-分组过滤练习题

题目

image.png

答案

select
    university,
    round(avg(question_cnt), 3) as avg_question_cnt,
    round(avg(answer_cnt), 3) as avg_answer_cnt
from
    user_profile
group by
    university
having
    avg(question_cnt) < 5
    or
    avg(answer_cnt) < 20

思路

  1. 使用 group by子句对学校分组
  2. 然后利用having子句过滤分组结果
  3. 使用round函数保留三位小数即可

SQL20-分组排序练习题

题目

image.png

答案

select
    university,
    round(avg(question_cnt), 4) as avg_question_cnt
from
    user_profile
group by
    university
order by
    avg_question_cnt

思路

  1. 首先查出universityavg_question_cnt字段
  2. 然后利用round函数保留四位小数,利用avg函数取得平均值
  3. 使用group by语句分组,使用order by语句排序(默认是升序排序)

多表查询

子查询

SQL21-浙江大学用户题目回答情况

题目

image.png

答案

-- 子查询写法
select
    device_id,
    question_id,
    result
from
    question_practice_detail
where
    device_id
    in
    (select device_id from user_profile where university = '浙江大学')
    
-- join写法
select
    qpd.device_id,
    qpd.question_id,
    qpd.result
from
    question_practice_detail as qpd
inner join
    user_profile as up
on
    qpd.device_id = up.device_id
where
    up.university = '浙江大学'

思路

  1. 子查询写法:先通过子查询获取user_profile表中universtiry为浙江大学的行数据,返回其device_id字段,主查询再根据这些device_idquestion_practice_detail表中查出具体记录
  2. join写法:利用inner join关键字把question_practice_detail表和user_profile表按 device_id 关联,找出所有属于浙江大学用户的记录,并返回需要字段即可

链接查询

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

题目

image.png

image.png

答案

select
    up.university,
    round(count(qpd.device_id) / count(distinct up.device_id), 4) as avg_answer_cnt
from
    user_profile as up
inner join
    question_practice_detail as qpd
on
    up.device_id = qpd.device_id
group by
    up.university
order by
    up.university asc

思路

  1. 通过内连接inner join查出两个表device_id一致匹配的记录
  2. 利用group by子句对学校进行分组,利用order by子句按照university升序排序
  3. 平均答题数量计算方式:学校用户答题总次数 除以 答过题的不同用户个数,可得出count(qpd.device_id) / count(distinct up.device_id)
  4. 利用round函数保留四位小数即可

SQL23-统计每个学校各难度的用户平均刷题数

题目

image.png

image.png

答案

select
    up.university,
    qd.difficult_level,
    round(count(qpd.id) / count(distinct qpd.device_id), 4) as avg_answer_cnt
from
    user_profile as up
inner join
    question_practice_detail as qpd
on
    up.device_id = qpd.device_id
inner join
    question_detail as qd
on
    qpd.question_id = qd.question_id
group by
    up.university,
    qd.difficult_level

思路

  1. 首先把需要的字段列出来
  2. 观察三表之间的关系 并通过inner join内连接匹配出符合的数据
  3. 从题目得知需要计算不同学校、不同难度的数据,所以需要对不同学校、不同难度进行分组,所以用到了group by进行分组操作
  4. 最后一个也是最难的点,计算用户平均答题量,公式为:总共答了多少题 / 去重后的用户数量,其实不难看出用户练习过的题目记录都存在于题库练习明细表question_practice_detail当中,直接利用count函数计算出用户答了多少题目,再利用count(distinct qpd.device_id)去重完再进行一个计数的操作得到去重后的用户数量,接着总共答了多少题 / 去重后的用户数量就可以得出平均答题数,最终用round函数进行保留四位小数的操作

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

题目

image.png

image.png

答案

select
    up.university,
    qd.difficult_level,
    round(count(qpd.question_id) / count(distinct(qpd.device_id)), 4) as avg_answer_cnt
from
    user_profile as up
inner join
    question_practice_detail as qpd
on
    up.device_id = qpd.device_id
inner join
    question_detail as qd
on
    qpd.question_id = qd.question_id
where
    up.university = '山东大学'
group by
    up.university,
    qd.difficult_level

思路

  1. 首先把需要的字段列出来
  2. 观察三表之间的关系 并通过inner join内连接匹配出符合的数据
  3. 从题目得知需要计算山东、不同难度的用户平均答题量,所以需要对学校、不同难度进行分组,所以用到了group by进行分组操作,学校限定了山东,所以还需要where筛选出山东大学的,(注意:where需要放在group by前面)
  4. 最后一个也计算用户平均答题量,对于每个分组,计算该组内用户的答题总数,并除以用户数量(记得去重),得到平均答题量(此处不过多解释,和SQL23思路差不多)

组合查询

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

题目

image.png

答案

select
    device_id,
    gender,
    age,
    gpa
from
    user_profile
where
    university = '山东大学' 
union all
select
    device_id,
    gender,
    age,
    gpa
from
    user_profile
where
    gender = 'male' 

思路

  1. where子句:
    • 第一个查询部分:筛选出 university 为 "山东大学" 的用户。
    • 第二个查询部分:筛选出 gender 为 "male" 的用户。
  2. union all:将两个查询结果合并。与 union 不同,union all 不会去除重复记录

必会的常用函数

条件函数

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

题目

image.png

答案

-- if函数
select
    if(age >= 25, "25岁及以上", "25岁以下") as age_cut,
    count(*) as number
from
    user_profile
group by
    age_cut

-- case函数
select
    case
        when age >= 25 then "25岁及以上"
        else "25岁以下" 
    end as age_cut,
    count(*) as number
from 
    user_profile
group by
    age_cut

思路

  1. if函数:
    • 首先利用if函数查出 25岁及以上 和 25岁以下的 数据 并 重命名为 age_cut
    • 然后对age_cut进行分组就可以得到 25岁及以上 和 25岁以下的 分组数据
    • 由于分组了,count(*)自然就可以拿到两组数据的数量
  2. case函数:
    • 和if函数差不多流程,主要就是if 变成了 case函数

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

题目

image.png

答案

-- 使用else
select
    device_id,
    gender,
    case
        when age < 20 then "20岁以下"
        when (age >= 20 and age <= 24) then "20-24岁"
        when age >= 25 then "25岁及以上"
        else "其他"
    end as age_cut
from
    user_profile


-- 不使用else
select
    device_id,
    gender,
    case
        when age is null then '其他'
        when age < 20 then "20岁以下"
        when (age >= 20 and age <= 24) then "20-24岁"
        when age >= 25 then "25岁及以上"
    end as age_cut
from
    user_profile

思路

  • CASE WHEN 是 MySQL 中用于条件判断的语句,类似于编程语言中的 if-else 或 switch,可以根据条件返回不同的结果

日期函数

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

题目

image.png

答案

-- 使用date_format函数
select
    date_format(date, '%d') as day,
    count(*) as question_cnt
from
    question_practice_detail
where
    date_format(date, '%Y-%m') = '2021-08'
group by
    date

-- 使用 year、month、day相关函数实现
select
    day(date) as day,
    count(*) as question_cnt
from
    question_practice_detail
where
    year(date) = '2021' and month(date) = '08'
group by
    date

思路

  • 使用date_format函数

    • 首先利用where子句筛选出2021-08月份的数据,利用date_format日期格式函数格式化成想要的格式直接拿到年和月 相比较筛出2021-08月份的数据
    • 接着对date进行分组,相同日期的就会被分为一组
    • 最后select里面查询两个字段,直接利用日期格式函数格式化得到天数据,count(*)计数函数拿到分组后的当天记录数即可
  • 使用yearmonthday函数

    • 首先利用where子句筛选出2021-08月份的数据,利用yearmonth函数得到年和月 相比较筛出2021-08月份的数据
    • 接着对date进行分组,相同日期的就会被分为一组
    • 最后select里面查询两个字段,直接利用day函数得到天数据,count(*)计数函数拿到分组后的当天记录数即可

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

题目

image.png

答案

-- 主要利用left join和date_add函数
select
    count(date2) / count(date1) as avg_ret
from (
    select
        distinct qpd.device_id,
        qpd.date as date1,
        l_qpd.date as date2
    from
        question_practice_detail as qpd
    left join (
        select
            distinct device_id,
            date
        from 
            question_practice_detail
    ) as l_qpd
    on
        qpd.device_id = l_qpd.device_id
        and
        date_add(qpd.date, interval 1 day) = l_qpd.date
) as id_last_next_date


-- 主要利用lead窗口函数和datediff函数
select
    avg(if(datediff(date2, date1)=1, 1, 0)) as avg_ret
from (
    select
        distinct device_id,
        date as date1,
        lead(date) over (partition by device_id order by date) as date2
    from (
        select
            distinct device_id, date
        from
            question_practice_detail
    ) as uniq_id_date
) as id_last_next_date

思路

  • 主要利用left joindate_add函数

    • 找到每个用户每天的刷题记录

    • 通过左连接(LEFT JOIN)将每一天的记录与下一天的记录关联起来,以确定哪些用户在次日也进行了刷题。

    • 计算留存率:统计有次日刷题记录的用户数,并除以总的刷题用户数,得到留存率

    • 关键点总结
      • LEFT JOIN:用于关联当前日期和次日的记录。使用 DATE_ADD(qpd.date, INTERVAL 1 DAY) 来获取次日的日期。
      • DISTINCT:确保每个用户的每日记录唯一,避免重复计算。
      • COUNT() 函数:分别统计有次日记录的用户数和总的刷题用户数,从而计算留存率
  • 主要利用lead空间函数和datediff函数

    • 找到每个用户每天的刷题记录
    • 使用窗口函数 LEAD() 获取每个用户的下一次刷题日期
    • 通过 DATEDIFF() 函数判断下一次刷题是否发生在次日
    • 计算平均值,得到次日留存率
    • 关键点总结
      • LEAD() 窗口函数:用于获取每个用户的下一次刷题日期。
      • DATEDIFF() 函数:用于计算两个日期之间的差异,判断是否为次日。
      • IF() 函数:根据条件返回 1 或 0,便于后续计算平均值。
      • AVG() 函数:计算所有 1 和 0 的平均值,得到次日留存率。

文本函数

SQL30-统计每种性别的人数

题目

image.png

答案

-- 主要是利用 substring_index 文本函数
select
    substring_index(profile, ',', -1) as gender,
    count(*) as number
from
    user_submit
group by
    gender

思路

  • 使用sunstring_index文本函数:substring_index(‘s’,‘分隔符’,数字):数字为正,从左往右有几个;数字为负,从右往左数几个
    • 首先,利用字符串函数 sunstring_index 从 profile 字段中提取性别信息,该字段格式为“身高,体重,年龄,性别”,通过获取最后一个逗号后的部分来提取性别。
    • 接着,使用 gruop by 对提取出的性别进行分组,并结合 count(*) 函数统计每个性别的参赛者数量
    • 最后,将统计结果按照 gender|number 的格式输出。

SQL31-提取博客URL中的用户名

题目

image.png

答案

-- 主要是利用 substring 文本函数
select
    device_id,
    substring(blog_url, 11) as user_name
from
    user_submit

思路

  • 使用substring文本函数:substring('s',startindex,length): 截取字符串,第一个参数是要被截取的字符串,第二个参数是开始截取的位置,第三个参数是截取的长度。如果截取的长度是空,则代表截取全部
    • 利用字符串文本函数 substring 从 blog_url 字段中提取用户名信息,从指定位置截取,第三个参数不写代表截取全部,这样就完事了还是比较简单方便的

SQL32-截取出年龄

题目

image.png

答案

-- 主要是利用 substring_index 文本函数
select
    substring_index(substring_index(profile, ',', 3), ',', -1) as age,
    count(*) as number
from
    user_submit
group by
    age

思路

  • 使用sunstring_index文本函数:substring_index(‘s’,‘分隔符’,数字):数字为正,从左往右有几个;数字为负,从右往左数几个
    • 利用字符串文本函数 substring_index 从 profile 字段中提取信息,从指定位置截取,首先截取出三组数据
    • 再次利用substring_index函数截取最后一个就可以拿到年龄数据了

窗口函数

SQL33-找出每个学校GPA最低的同学

题目

image.png

答案

-- 写法一:利用内连接实现
select
    up.device_id,
    up.university,
    up.gpa
from
    user_profile as up
inner join (
    select
        university,
        min(gpa) as gpa
    from
       user_profile
    group by
        university
)  as l_up
on up.university = l_up.university and up.gpa = l_up.gpa
order by
    university

-- 写法二:利用row_number窗口函数实现
select
    device_id,
    university,
    gpa
from
    (
        select
            *,
            row_number() over (partition by university order by gpa) as rn
        from
            user_profile
    ) as l_up
where
    rn = 1
order by
    university

思路

  • 解法一:
    1. 先把需要返回的列给写好
    2. inner join内连接查询,利用min函数 配合 group by分组语句查出一组相同学校的最小gpa值,这样就能确保只选出那些 gpa 是该大学最低分数的学生记录
    3. 最后利用on指定条件条件,如果学校名称并且gpa对上 就能找到对应符合的行数据了
    4. 利用order by按大学名称排序输出就行,默认asc升序
  • 解法二:
    1. 先把需要返回的列给写好
    2. 利用子查询及row_number窗口函数,使用 row_number() 窗口函数来为每个大学的学生分配一个行号,partition by university:表示按 university 字段对数据进行分组(即每个大学一组)。order by gpa:在每个分组内根据 gpa 字段的值进行排序,默认是升序(asc),即 gpa 较低的排在前面,as rn:给这个新生成的行号列命名为 rn
    3. 利用where子句筛选 只保留那些行号 rn 等于 1 的记录,也就是每所大学中 gpa 最低的学生记录
    4. 利用order by按大学名称排序输出就行,默认asc升序

聚合函数与窗口函数的结合使用

SQL41-计算每日累计利润

题目

image.png

答案

select
    profit_id,
    profit_date,
    profit,
    sum(profit) over (order by profit_date) as cumulative_profit
from
    daily_profits

思路

  1. 主要是利用窗口函数 sum oversum(profit) over (order by profit_date) as cumulative_profit按照 profit_date 的顺序,逐行累加 profit 值,并将当前行的累计结果作为 cumulative_profit 输出

注意

  • 加 order by profit_date:确保累计求和是按时间顺序进行的,每一行的 cumulative_profit 是从第一天到当前行的利润总和。
  • 不加 order by profit_date:会导致每一行的 cumulative_profit 都是整个表的利润总和,失去“累计”的意义。

因此,在你需要按某种顺序进行累计求和时,必须指定 order by 子句。否则,结果可能不符合预期。

数学函数

SQL42-基本数学函数

题目

image.png

答案

select
    id,
    value,
    abs(value) as absolute_value,
    ceiling(value) as ceiling_value,
    floor(value) as floor_value,
    round(value, 1) as rounded_value
from
    numbers
order by
    id

思路

  1. 查出所有需要的列
  2. abs()函数返回一个数字的绝对值
  3. ceiling()函数返回大于或等于给定数字的最小整数值
  4. floor()函数返回小于或等于给定数字的最大整数值
  5. round()函数用于将数字四舍五入到指定的小数位数
  6. 利用order byid进行排序

综合练习

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

题目

image.png

答案

select
    up.device_id,
    up.university,
    count(qpd.id) as question_cnt,
    sum(case when qpd.result = 'right' then 1 else 0 end) as right_question_cnt
from
    user_profile as up
left join
    question_practice_detail as qpd
on
    up.device_id = qpd.device_id and month(qpd.date) = '08'
where
    up.university = '复旦大学'
group by
    up.device_id,
    up.university

思路

  1. 首先把需要需要返回的字段定义好
  2. 两张表有关联关系,使用left join查询根据device_id找出对应的数据,并且需要使用and跟上一个并且的条件关系,只拿8月份的数据
  3. 利用where子句筛出学校为复旦大学的数据
  4. 利用group by对唯一id和学校进行分组
  5. 最后计算question_cntright_question_cnt值即可,接着往下走
  6. 利用count函数计算出练习的总题目数得到question_cnt
  7. 使用case when语句做条件判断 resultright说明答题正确值为1否则为0,使用sum求和即可得到回答正确的题目数总和

注意

  • qpd.device_id and month(qpd.date) = '08'条件需写在on语句后面,不能写在where后面,否则错误

    image.png

  • count(qpd.id) as question_cnt,这里count函数不能写*,否则计算不准确(没匹配上qpd.id就是null,count是不会计算null值的数据的)

    • 更准确地统计练习题目数量(因为 qpd.id 在无练习时为 NULL

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

题目

image.png

答案

select
    qd.difficult_level as difficult_level,
    round(sum(case when qpd.result = "right" then 1 else 0 end) / count(qd.question_id), 4) as difficult_level
from
    user_profile as up
inner join
    question_practice_detail as qpd
on
    up.device_id = qpd.device_id
inner join
    question_detail as qd
on
    qpd.question_id =  qd.question_id
where
    up.university = "浙江大学"
group by
    qd.difficult_level
order by
    difficult_level asc

思路

  1. 首先formuser_profile表,接着用where子句筛选出浙江大学的用户
  2. 通过inner join内连接我们把几个表关联关系的数据都查出来,根据题目可知 不同难度题目下,可以把难度等级进行分组,这样就可以得出每个难度等级的组数据了,这里使用gruop bydifficult_level难度等级分组即可
  3. 由于根据难度等级分组了,此时select里面的difficult_level直接写出即可,接着计算出答题的正确率,也就是得出select当中的difficult_level字段,计算公式为 (不同难度题目下正确的答题数 / 不同难度题目下的答题数量)
  4. 利用case when语句进行一个条件判断,正确计数为1,否则为0,利用sum函数对正确的答题数进行累加统计就得到了不同难度题目下正确的答题数,由于对不同难度的题目已经进行了分组,不同难度题目的答题总数量直接取出question_id,利用count函数计数即可,最后 (不同难度题目下正确的答题数 / 不同难度题目下的答题总数量)就得出了不同难度题目下答题的正确率情况,最后使用round()  函数用于将数字四舍五入到指定的小数位数即可
  5. 最后使用order by子句对准确率进行一个升序排序即可

SQL39-21年8月份练题总数

题目

image.png

答案

select 
    count(distinct device_id) as did_cnt, 
    count(*) as question_cnt
from
    question_practice_detail
where
    date between '2021-08-01' and '2021-08-31'

思路

  1. 首先把两个需要返回的字段定义好
  2. where条件查出2021年8月份区间的数据
  3. 利用distinct对总用户数进行去重过滤
  4. 利用count计数练习题目的总次数即可
  • 编写有误还请大佬指正,万分感谢。