SQL进阶day9————聚合与分组

 

目录

1聚合函数

1.1SQL类别高难度试卷得分的截断平均值

1.2统计作答次数

1.3 得分不小于平均分的最低分

2 分组查询

2.1平均活跃天数和月活人数

2.2 月总刷题数和日均刷题数

2.3未完成试卷数大于1的有效用户

1聚合函数

1.1SQL类别高难度试卷得分的截断平均值

我的错误代码:截断平均值是有专门的函数吗?

select tag,difficulty,avg(score) clip_avg_score
from examination_info ei join exam_record
using(id)
group by tag
where tag = 'SQL' and difficulty='hard'
and score not in (max(score),min(score))

我的思路改正:用 (全部值 - 最大值 - 最小值) / (总数-2) ,但是缺点就是,如果最大值和最小值有多个,这个方法就很难筛选出来

SELECT ei.tag,ei.difficulty,
       ROUND((SUM(er.score)-MIN(er.score)-MAX(er.score)) / (COUNT(er.score)-2),1) AS clip_avg_score
FROM examination_info ei join exam_record er
on ei.exam_id = er.exam_id
where ei.tag = "SQL"
AND ei.difficulty = "hard";

标准正确代码:

使用in子句将最大值和最小值排除掉,再求平均值

  • 懒人写法,可以用with...as句式将要多次使用的表只写1次即可(WITH AS 语法是MySQL中的一种临时结果集,它可以在SELECT、INSERT、UPDATE或DELETE语句中使用。通过使用WITH AS语句,可以将一个查询的结果存储在一个临时表中,然后在后续的查询中引用这个临时表。这样可以简化复杂的查询,提高代码的可读性和可维护性。但是不知道哪个MySQL版本开始支持with...as句式的,我的本地电脑里面是Navicat 15 for MySQL,不支持
  • union把max和min的结果集中在一行当中,这样形成一列多行的效果,不用多写一次代码
# t1筛选出SQL高难度的数据
WITH t1 as(
    SELECT er.*,ei.tag,ei.difficulty
    FROM exam_record er INNER JOIN
    examination_info ei
    ON er.exam_id = ei.exam_id
    WHERE tag = "SQL" and difficulty = "hard"
)

# 在t1的基础上计算均值
SELECT tag,difficulty,round(avg(score),1)
FROM t1

# 用in子句将最大值和最小值排除掉,再求平均值 not in
WHERE score not in (
    SELECT max(score)
    FROM t1
    UNION
    SELECT min(score)
    FROM t1
)

Q:为什么这里where换成and也不报错,因为前面有on?那么where和on有啥区别呢,可以只有一个吗?

A:

(1)where和having是在临时表生产之后,对临时表中的数据进行过滤用的。

如SQL语句:select * form tab1 left join tab2 on (tab1.size = tab2.size) where tab2.name=’AAA’

(2) on是在生成中临时表之前就去作用的,它会在数据源那里就把不符合要求的数据给过滤掉,即是先把不符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据所以on运行的速度最快。

如SQL语句:select * form tab1 left join tab2 on (tab1.size = tab2.size and tab2.name=’AAA’)

(3)在两个表联接时才用on的,所以在一个表的时候,就剩下where跟having比较了。在这单表查询统计的情况下,如果要过滤的条件没有涉及到要计算字段,那它们的结果是一样的,但是where可以使用rushmore技术,而having就不能,在速度上后者要慢。

(4)  如果要涉及到计算的字段,where的作用时间是在计算之前就完成的,而having就是在计算后才起作用的,所以在这种情况下,两者的结果会不同。   

(5) 在多表联接查询时,on比where更早起作用。系统首先由on根据各个表之间的联接条件,把多个表合成一个临时表后,再由where进行过滤,然后再计算,计算完后再由having进行过滤。

1.2统计作答次数

我的报错代码:求已完成的试卷数时应该要分组一下,exam_id

select count(er.id) total_pv,
count(er.submit_time) complete_pv,
count(t2.exam_id) complete_exam_cnt
from exam_record er,(select count(exam_id) from exam_record 
group by exam_id) t2

正确代码1:

主要在于已完成的试卷数的统计,因为这个带有条件,且需要统计聚合结果,可以使用 聚合函数与case when 结合count中是可以加条件的

select 
count(*)  total_pv,
count(score)  complete_pv,
count(distinct case when score is null then null else exam_id end)  complete_exam_cnt
from exam_record

复习case when

(1)case expr when v1 then r1 [when v2 then r2] ...[else rn] end

       例如:case 2 when 1 then 'one' when 2 then 'two' else 'more' end 返回two

       case后面的值为2,与第二条分支语句when后面的值相等相等,因此返回two

(2)case when v1 then r1 [when v2 then r2]...[else rn] end

       例如:case when 1<0 then 'T' else 'F' end返回F

       1<0的结果为false,因此函数返回值为else后面的F

正确代码2:

select count(*) as total_pv,
count(score) as complete_pv,
count(distinct exam_id,score IS NOT NULL or null) as complete_exam_cnt
# 是逗号,连接不是and连接
from exam_record

在select和count后面都可以加条件的,但是要明白内核:

count(distinct exam_id,score IS NOT NULL or null) as complete_exam_cnt (正确)

不能是

count(distinct exam_id and score IS NOT NULL or null) as complete_exam_cnt (错误,结果永远为2, 这个题只是碰巧结果为2,改一个数据就不对了)

1 用and:

(1)一般在where后筛选过滤,还是得到的满足条件的score

(2)如果在select后直接加条件判断:这里的score is not null 是判断

  • 符合条件的返回 true ,即为1
  • 不符合的返回 false ,即为0 

(3) 加上exam_id,进行and逻辑运算

  • exam_id 本身为值,可以理解为真 在 and 逻辑下,所以上一步的1,0并不会变化,后面加上or NULL,否则会把0也计算上。

(4) 在上一步的基础上去重,则只会剩下1和0

  • 经过and运算之后,只剩下一列数据,多行1和0
  • distinct 去重后,就只剩下两行数据 1 和 0

(5)所以这时候再进行count计算,结果恒为2 (两行数据)

 2 正确答案的执行逻辑:

(1)用,连接(从之前的逻辑判断,变为多列组合)

(2)这时候distinct 去重后,就不是只剩下两行数据 1 和 0,而是会把score为null也会考虑进去。

(3)结果应该是3,如果没有or NULL,就是5行了(null为0会被计数)

1.3 得分不小于平均分的最低分

 

我的代码:where后面的条件错了,但是思路大概这样。

select score min_score_over_avg 
from exam_record er join examination_info ei
on er.id = ei.id
group by exam_id
where score>=avg(score) and ei.tag = 'SQL'
order by score asc
limit 1

修改我的代码:

select er.score min_score_over_avg 
from exam_record er 
left join examination_info ei
on er.exam_id = ei.exam_id # 不是按照id连接
where  ei.tag = 'SQL' 
and score>= (SELECT avg(er.score) from exam_record er
left join examination_info ei
on er.exam_id=ei.exam_id
where ei.tag='SQL')
order by score asc
limit 1

(1)表连接是按照exam_id ,不是按照id连接 

(2)score>=某个值,这里不能直接score>=avg(score),而是应该通过表查询返回得到avg(score),然后在进行比较。

改进我的代码:

这里有表查询的部分重复了两次,可以用with...as句式将要多次使用的表命名,这样可以只写一次,多次调用。

此外,order by score asc  limit 1  可以换为min函数。

with t as
(SELECT score from exam_record er
left join examination_info ei
on er.exam_id=ei.exam_id
where ei.tag='SQL')

select min(score) min_score_over_avg 
from t 
where score>= 
(SELECT avg(t.score) from t)

2 分组查询

2.1平均活跃天数和月活人数

我的代码:此处活跃指有交卷行为,用户平均月活跃天数avg_active_days啥意思?

with t as
(select * 
from exam_record
where year(start_time)=2021)

select month(start_time) 'month',
count(submit_time) mau
from t
group by month(submit_time)

正确代码:

select DATE_FORMAT(start_time,"%Y%m") as month,
round(count(distinct uid,date_format(start_time,"%Y%m%d"))/count(distinct uid),2) as avg_active_days,
count(distinct uid) as mau
from exam_record
where submit_time is not NULL
and YEAR(submit_time) =2021
group by month;

(1)202107是用date_format函数:DATE_FORMAT(start_time,"%Y%m") as month

(2)主要难的一点是天数的计算。

到底是count(distinct uid,date_format(start_time,"%Y%m%d"))

还是count(start_time)作为分子呢

关键是理解题目的意思是:天数。

假设一个uid 比如1001在2021-07-06这一天有二个记录,如果是count(start_time)那么就是天数是2,但是如果是count(distinct uid,date_format(start_time,"%Y%m%d"))天数就是1了

复盘探索:

(1)先找出2021年,活跃的用户ID和时间(具体到哪天)

(2)如果不考虑uid,直接按照活跃时间去重,那么不同用户在同一天活跃记录会被去重到只剩下1条,

(3)同理,如果只安装用户ID去重,那么同一用户在不同天的记录也会被去重到只有1条。这里查询的实际是月活跃的用户有哪些。

(4) 所以要去重的目的是,同一个用户,在同一天,重复提交活跃多次的记录。(因为这里是按天算,同一天同一个用户只算一次。)

(5)用户平均月活跃天数=月活跃天数 /月活跃用户

:count(distinct uid,date_format(start_time,"%Y%m%d"))/count(distinct uid)

月活跃天数:

月活跃用户: 

2.2 月总刷题数和日均刷题数

我的代码:分组好像报错,后面那个求总数我也不知道咋整

分组报错问题:MySQL提供了any_value()函数来抑制ONLY_FULL_GROUP_BY值被拒绝

select 
date_format(submit_time,'%y%m') submit_month,
count(score) month_q_cnt,
count(score)/day(month(submit_time)) avg_day_q_cnt
from practice_record
group by date_format(submit_time,'%y%m%d') 
having year(submit_time)=2021

(1)当月天数求错了,我是想先求出当前月,再求出当月天:这样操作结果是不对的。

    

应该用last_day函数求出最后一天,然后用day函数求出这个日期的天数。

复习【日期时间函数】

●   year(date)——获取年的函数

●   month(date)——获取月的函数

●   day(date)——获取日的函数

●   date_add(date,interval expr type)——对指定起始时间进行加操作

●   date_sub(date,interval expr type)——对指定起始时间进行减操作

●   datediff(date1,date2)——计算两个日期之间间隔的天数

●   date_format(date,format)——将日期和时间格式化

代码改正:

select 
date_format(submit_time,'%y%m') submit_month,
any_value(count(score)) month_q_cnt,
any_value(round(count(score)/day(last_day(submit_time)),3)) avg_day_q_cnt
from practice_record
where year(submit_time)='2021'
# where date_format(submit_time,'%y')='2021'
group by submit_month

该年的总体情况,可以用union all来连接,完整代码:

select date_format(submit_time,'%Y%m') submit_month,
any_value(count(question_id)) month_q_cnt,
any_value(round(count(question_id)/day(LAST_DAY(submit_time)),3)) avg_day_q_cnt 
from practice_record
where date_format(submit_time,'%Y')='2021'
group by submit_month
union all
select '2021汇总' as submit_month,
count(question_id) month_q_cnt,
round(count(id)/31,3) avg_day_q_cnt
from practice_record
where date_format(submit_time,'%Y')='2021'
order by submit_month;

复习:

1、区别1:取结果的交集

1)union: 对两个结果集进行并集操作, 不包括重复行,相当于distinct, 同时进行默认规则的排序;

2)union all: 对两个结果集进行并集操作, 包括重复行, 即所有的结果全部显示, 不管是不是重复;

2、区别2:获取结果后的操作

1)union: 会对获取的结果进行排序操作

2)union all: 不会对获取的结果进行排序操作

3、总结

union all只是合并查询结果,并不会进行去重和排序操作,在没有去重的前提下,使用union all的执行效率要比union高。

2.3未完成试卷数大于1的有效用户

我的代码:罗里吧嗦答案还不对。。

with t as
(select uid,er.exam_id,start_time,submit_time,tag
from exam_record er , examination_info ei
where er.exam_id=ei.exam_id
and date_format(start_time,'%Y')='2021')

select uid,
(select count(submit_time) from t
where submit_time is NULL) incomplete_cnt,
(select count(submit_time) from t
where submit_time is not NULL) complete_cnt
from t
where (select count(submit_time) from t
where submit_time is NULL)<5 

and (select count(submit_time) from t
where submit_time is not NULL)>1
order by incomplete_cnt

我的代码改正:

select uid,
sum(case when submit_time is NULL then 1 else 0 end ) incomplete_cnt,
sum(case when submit_time is NULL then 0 else 1 end ) complete_cnt
from exam_record er join examination_info ei
on er.exam_id=ei.exam_id
where date_format(start_time,'%Y')='2021'
group by uid
having incomplete_cnt >1 and incomplete_cnt<5 and complete_cnt>1
order by incomplete_cnt

接下来是detail,作答过的试卷tag集合,是提交日期:类型;一直重复显示

我的完整代码:

select uid,
sum(case when submit_time is NULL then 1 else 0 end ) incomplete_cnt,
sum(case when submit_time is NULL then 0 else 1 end ) complete_cnt,
group_concat(DISTINCT concat_ws(':',date_format(start_time,"%Y-%m-%d"),tag) order by start_time Separator ';') detail

from exam_record er join examination_info ei
on er.exam_id=ei.exam_id
where date_format(start_time,'%Y')='2021'
group by uid
having incomplete_cnt >1 and incomplete_cnt<5 and complete_cnt>1
order by incomplete_cnt desc

注意:select后面的属性,不管计算了多长,每个之间都要有逗号!!! 

大佬代码:

SELECT uid,
SUM(CASE WHEN submit_time IS NULL THEN 1 ELSE 0 END) "incomplete_cnt",
SUM(CASE WHEN submit_time IS NULL THEN 0 ELSE 1 END) "complete_cnt",
group_concat(distinct concat_ws(':',date(start_time),tag) 
order by start_time separator ';') as detail
FROM exam_record er INNER JOIN
examination_info ei 
ON er.exam_id = ei.exam_id
WHERE year(start_time) = 2021
GROUP BY uid
HAVING complete_cnt >= 1 AND incomplete_cnt > 1 AND
incomplete_cnt < 5
ORDER BY incomplete_cnt desc

其中

(1)用sum和case when函数来求完成和未完成的试卷数

(2)detail的实现是用concat_ws或者concat函数将submit_time和tag连接并且同时distinct:

函数group_concat([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator'分隔符'])

concat()函数

  • 功能:将多个字符串连接成一个字符串。

  • 语法:concat(str1, str2,…)

  • 返回结果为连接参数产生的字符串,如果有任何一个参数为null,则返回值为null。

concat_ws()函数

  • 功能:和concat()一样,将多个字符串连接成一个字符串,但是可以一次性指定分隔符(concat_ws就是concat with separator)
  • 语法:concat_ws(separator, str1, str2, …)
  • 说明:第一个参数指定分隔符。需要注意的是分隔符不能为null,如果为null,则返回结果为null。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/675949.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JUC从实战到源码:悲观锁和乐观锁真正了解了吗

【JUC】- 多线程与锁的知识 &#x1f604;生命不息&#xff0c;写作不止 &#x1f525; 继续踏上学习之路&#xff0c;学之分享笔记 &#x1f44a; 总有一天我也能像各位大佬一样 &#x1f3c6; 博客首页 怒放吧德德 To记录领地 &#x1f31d;分享学习心得&#xff0c;欢迎指…

语音群呼之语音导航的应用

在数字化时代&#xff0c;语音群呼技术已成为企业、组织和个人高效沟通的重要工具。语音群呼不仅能够快速地将信息传递给目标群体&#xff0c;而且通过语音导航功能&#xff0c;还能确保信息传达的准确性和用户体验的优质性。本文将深入探讨语音群呼的语音导航功能&#xff0c;…

三菱M5-559 KURU TOGA advance断芯清理维修

三菱559自动铅笔使用过程中突然不出芯了&#xff0c;后面装芯出不来&#xff0c;前面插笔芯进不去&#xff0c;网上搜索&#xff0c;发现这支笔要按照下面的方法拆开清理&#xff0c;这里记录一下方便大家查看&#xff1a; 1、拧掉笔头外面的罩子。 2、要大胆一点&#xff0c…

图形学初识--深度测试

文章目录 前言正文为什么要有深度测试&#xff1f;画家算法循环遮挡 深度测试当代最常见实现方式&#xff1f;总述什么是z-buffer呢&#xff1f;z-buffer从哪来呢&#xff1f;如何利用z-buffer实现深度测试&#xff1f;举个例子 结尾&#xff1a;喜欢的小伙伴点点关注赞哦! 前言…

【MyBatis】MyBatis操作数据库(二):动态SQL、#{}与${}的区别

目录 一、 动态SQL1.1 \<if>标签1.2 \<trim>标签1.3 \<where>标签1.4 \<set>标签1.5 \<foreach>标签1.6 \<include>标签 二、 #{}与${}的区别2.1 #{}是预编译sql&#xff0c;${}是即时sql2.2 SQL注入2.3 #{}性能高于${}2.4 ${}用于排序功能…

SpringBoot案例,通关版

项目目录 此项目为了伙伴们可以快速入手SpringBoot项目,全网最详细的版本,每个伙伴都可以学会,这个项目每一步都会带大家做,学完后可以保证熟悉SpringBoot的开发流程项目介绍:项目使用springboot mybatis进行开发带你一起写小项目先把初始环境给你们第一步新建springboot项目返…

短剧出海的优势分析

海外短剧作为一种新兴的内容形式&#xff0c;正以其独特的魅力迅速占领市场&#xff0c;为企业带来了前所未有的商业机遇。本文将深入探讨短剧出海的优势&#xff0c;并为企业和老板们提供实用的操作指南。短剧出海是一个包含多个步骤的复杂过程&#xff0c;短剧出海需要综合考…

第100天:权限提升-数据库RedisPostgre第三方软件TV向日葵服务类

目录 思维导图 案例一: 数据库-Redis 数据库权限提升-计划任务 案例二: 数据库-PostgreSQL 数据库权限提升-漏洞 PostgreSQL 提权漏洞&#xff08;CVE-2018-1058&#xff09; PostgreSQL 高权限命令执行漏洞&#xff08;CVE-2019-9193&#xff09; 案例三: 三方应用-…

使用system verilog进行流水灯和VGA打印字符

使用system verilog进行流水灯和VGA打印字符 目录 **使用system verilog进行流水灯和VGA打印字符****system verilog的优点****VGA程序编写**VGA 控制器模块字符生成模块顶层模块测试基准程序**效果** **流水灯程序设计****效果** **总结** system verilog的优点 面向对象编程…

C# WinForm —— 27 28 29 30 ListView 介绍与应用

1. 简介 和ListBox的外观类似&#xff0c;都可以多列显示&#xff0c;但 ListView 功能更强大&#xff0c;提供了5种不同的显示方式 2. 属性 属性解释(Name)控件ID&#xff0c;在代码里引用的时候会用到Enabled控件是否启用CheckBoxes复选框是否显示在项旁边ContextMenuStri…

浏览器渲染优--防抖节流懒加载

合理选择css选择器 相比于.content-title-span&#xff0c;使用.content .title span时&#xff0c;浏览器计算样式所要花费的时间更多。使用后面一种规则&#xff0c;浏览器必须遍历页面上所有 span 元素&#xff0c;先过滤掉祖先元素不是.title的&#xff0c;再过滤掉.title…

拿笔记下来!产品采购制造类合同怎样写比较稳妥?

拿笔记下来&#xff01;产品采购制造类合同怎样写比较稳妥&#xff1f; 近日&#xff0c;几经波折&#xff0c;泰中两国终于完成了潜艇采购谈判&#xff01;你知道吗&#xff1f;产品制造类合同或协议在起草前如果没有充分考虑各种因素&#xff0c;可能会导致一系列问题和不利…

奶茶店、女装店、餐饮店是高危创业方向,原因如下:

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 现在的俊男靓女们&#xff0c;心中都有一个执念&#xff1a; (1)想证明自己了&#xff0c;开个奶茶去…… (2)想多赚点钱了&#xff0c;加盟餐饮店去…… (3)工作不顺心了&#xff0c;搞个女装店去…… 但凡抱着…

【scau数据库实验一】mysql_navicat_数据库定义实验、基本命令

实验一开始之前&#xff0c;如果还有不会使用navicat建议花五分钟补课哦~ 补课地址&#xff1a;【scau数据库实验先导】mysql_navicat_数据库新建、navicat的使用-CSDN博客 实验目的&#xff1a; 理解和掌握数据库DDL语言&#xff0c;能够熟练地使用SQL DDL语句创建、修改和删…

mac电脑用谷歌浏览器对安卓手机H5页面进行inspect

1、mac上在谷歌浏览器上输入 chrome://inspect 并打开该页面。 2、连接安卓手机到Mac电脑&#xff1a;使用USB数据线将安卓手机连接到Mac电脑。 3、手机上打开要的h5页面 Webview下面选择要的页面&#xff0c;点击inspect&#xff0c;就能像谷歌浏览器页面打开下面的页面&#…

Vue——初识组件

文章目录 前言页面的构成何为组件编写组件组件嵌套注册 效果展示 前言 在官方文档中&#xff0c;对组件的知识点做了一个很全面的说明。本篇博客主要写一个自己的案例讲解。 vue 官方文档 组件基础 页面的构成 说到组件之前&#xff0c;先大致说明下vue中页面的构成要素。 在…

Claude 3可使用第三方API,实现业务流程自动化

5月31日&#xff0c;著名大模型平台Anthropic宣布&#xff0c;Claude3模型可以使用第三方API和工具。 这也就是说&#xff0c;用户通过文本提问的方式就能让Claude自动执行多种任务&#xff0c;例如&#xff0c;从发票中自动提取姓名、日期、金额等&#xff0c;该功能对于开发…

【问题随记】System policy prevents Wi-Fi scans,解决连接 WIFI 需要权限的问题

问题随记 System policy prevents Wi-Fi scans&#xff0c;每次打开我的开发板连接 wifi 都会出现下面的弹窗&#xff0c;这也阻挡了我的WIFI自动连接&#xff0c;然后就需要连上屏幕&#xff0c;输入 wifi 密码&#xff0c;这样才能进行 VNC、SSH 等一系列的连接。 问题解决 …

『 Linux 』缓冲区(万字)

文章目录 &#x1f9a6; 什么是缓冲区&#x1f9a6; 格式化输入/输出&#x1f9a6; 刷新策略&#x1fab6; 块缓冲(fully buffered)&#x1fab6; 无缓冲(unbuffered)&#x1fab6; 行缓冲(line buffered) &#x1f9a6; 现象解释&#x1f9a6; exit()与_exit()&#x1f9a6; 进…

CPU 使用率过高问题排查

文章目录 CPU 使用率过高问题排查1. CPU使用率过高常见问题2. 压力测试2.1 stress安装参数说明测试示例 2.2 stress-ng安装参数说明测试示例 3. 问题排查3.1 使用 top 命令3.2 使用 ps 命令3.3 使用 perf top3.4 vmstat 命令常用信息内存信息磁盘信息 CPU 使用率过高问题排查 …