单表复杂查询的场景分析二:涉及数据分组与分区/多重函数计算/SQL变种

SQL演练,带详细分析,笔记和备忘。行文不易,感谢支持!

本文是单表下的复杂场景问题分析,具体看下面的每个需求。

接上文,本文为连载的第二篇。

目录

数据表及说明

需求8:找出指定月份每个人的总时长低于该月份所有人平均总时长的人

方案1

方案2

需求9: 基于所有数据找出每个人的最新1天的记录

需求10:查询每个人(name)在该表中时长最长的一条记录,结果按时长降序排序 (分组内取最大) 

方案1

方案2

方案3

需求11:基于所有数据找出每人每天的最新一条记录


数据表及说明

创建一个数据表demo_records,内容如下:

该表记录了每个人在每天做某事情的的消耗时长记录。为方便配合后续的问题场景,已有测试数据方面也做了对应特征的设置。

每人每天可能会产生多条记录,每个不同记录的时长可能会相等,duration为整数,创建时间为时刻具体时间不是天级别。

大致总结下该表的主要信息:

  1. 是四个人在三天中所消耗的时长记录,王五、孙悟空、沙僧各4条,哪吒3条。
  2. 最大时长为孙悟空在3.3号的记录。
  3. 哪吒的最大时长为5,记录有两条,分别在3.2、3.4
  4. 孙悟空在3.2号有两条记录
  5. 时长最小值为3,有三条记录。

需求8:找出指定月份每个人的总时长低于该月份所有人平均总时长的人

1,计算3月份所有人的总时长值及其平均值

2,查询每个人在3月份的总时长值

3,将每个人的3月份总score与之前计算的平均总score进行比较

方案1

SELECT name, SUM(duration) AS monthlyDurationTotalEveryPeople
FROM  demo_records
WHERE month(created_at) = 3
GROUP BY  name
HAVING  monthlyDurationTotalEveryPeople < 
(
       SELECT AVG(totalDuration) AS avgTotalDuration
                FROM(   
                           SELECT SUM(duration) AS totalDuration  
                           FROM demo_records 
                           WHERE month(created_at) = 3 
                           GROUP BY  name
         )t
)

方案2

with monthlyDurationAvgEveryPeople AS (
SELECT AVG(totalDuration) AS avgTotalDuration
         FROM(   
                    SELECT SUM(duration) AS totalDuration  
                    FROM demo_records 
                    WHERE month(created_at) = 3 
                    GROUP BY  name
  )t
)
SELECT name, SUM(duration) AS monthlyDurationTotalEveryPeople
FROM  demo_records
WHERE month(created_at) = 3
GROUP BY  name
HAVING  monthlyDurationTotalEveryPeople < (
       SELECT avgTotalDuration from monthlyDurationAvgEveryPeople
)

结果如下

方案2是先将3月份所有人的总时长、总时长的平均值,结果放在表达式中,接着按既定条件聚合,聚合结果中与monthlyDurationAvgEveryPeople对比,实际逻辑和方案1一样,只是查询形式不同

因为最后是和每个人月总时长的平均值进行比较,因此在求平均值时需要先group by name,否则就是64除以1还是64,我们需要的是64除以4。

需求9: 基于所有数据找出每个人的最新1天的记录

结果有四条记录。

SELECT t.id, t.name, t.duration, t.created_at
FROM (
    SELECT id, name, duration, created_at,
           ROW_NUMBER() OVER (
                    PARTITION BY name ORDER BY created_at desc
           ) AS durationFirst
    FROM demo_records 
) t
WHERE t.durationFirst = 1

结果如下:

注意不要使用 DATE(created_at)='2022-03-04'来查,虽然结果可能一致但sql语义不对。

需求10:查询每个人(name)在该表中时长最长的一条记录,结果按时长降序排序 (分组内取最大) 

这里我们先列举几个错误的SQL语句:

1

select id,name,max(duration) d,created_at
from demo_records
group by name
order by d desc  

select id,name,max(duration) as d,created_at
from demo_records r
group by name
having d=max(duration)
order by d desc

第二个比第一个多了个条件过滤。

这条语句从所有记录中按人分组并求各自最大值,name和最大值是匹配的,即如果仅是把人和最大值查出来,那么这个语句是符合的,但目前我们需要查询的是符合的每条记录,这样查出来id、时间和其它两列数据不匹配,查出来数据是错误的。不正确的原因是group by按name分组后记录数会变少,而我们并没有按id、时间等字段分组,因此这两字段成了多选一,自然不正确,其它字段和max(duration内容不匹配。

也贴一下结果:

2,使用子查询

select a.id,a.name,a.duration,a.created_at  
from demo_records a,(
       select id,name,max(duration) as d,created_at from demo_records
       group by name) r
where a.id=r.id and a.created_at = r.created_at

select a.id,a.name,a.duration,a.created_at  
from demo_records a
join(
       select id,name,max(duration) as d,created_at from demo_records
       group by name) b
on a.id = b.id

这几个语句可能很多人都思考过,内部子查询的语句实际和上一条语句一样,已经group by name了,但是又select了其余全部字段,查出来数据必然不匹配。

3,这个可能最迷茫

select a.id,a.name,a.duration,a.created_at  
from demo_records a
join(
       select name,max(duration) d from demo_records
       group by name) b
on a.duration  = b.d and a.name=b.name
order by duration  desc

结果如下:

首先此语句的查询结果内的每条数据是匹配的,但是结果看着问题不大SQL就一定正确吗?

来分析一下:

首先子查询内部查询了最大时长和对应的人,数据正确,然后和本表连接,为了找到人和最大时长在表中对应的记录,所以采用了这个条件:

on a.duration  = b.d and a.name=b.name

即这个结果包含了正确结果(为什么是包含?因为需求是求每个人时长最长的一条记录,而哪吒最长的有两条记录)

但这个条件查出来的是未经分组的,需要再加一个按人分组才行,即方案1。

方案1

select a.id,a.name,a.duration,a.created_at  
from demo_records a
join(
       select name,max(duration) d from demo_records
       group by name) b
on a.duration  = b.d and a.name=b.name
group by name
order by duration  desc

查询结果:

如果对结果中最大时长的记录没有特别要求(如哪吒最大时长是两条记录,结果中要不要求具体显示哪条?),那么此时该语句和结果均是正确的。

方案2

SELECT t.id, t.name, t.maxDuration, t.created_at
FROM (
    SELECT id, name, duration, created_at,
           max(duration) OVER (PARTITION BY name) AS maxDuration
    FROM demo_records 
    ORDER BY created_at desc
) t     
where t.duration =t.maxDuration
group by name
ORDER BY duration desc

分析

首先基于OVER (PARTITION BY name)对已有数据按名称分区,接着基于现有分组后的数据、增加max(duration)来计算每个name中的最大时长,相当于增加了的maxDuration列在每个分组下是当前name的最大值;

分步看,只执行子查询,结果如下:

接着将当前查询结果记为t,取出时长和最大时长相等的记录,最终按时长降序排序。

注意到时长相等的可能有多条记录,因此结果中要处理同name的记录只保留一条,即group by name

此语句在group之前拿到的数据中会是5条记录(哪吒符合条件的有两条记录),即每人每天时长记录本身可能有多条,相同时长的更可能有多条,因此需要group一下。经过最后分组之后,数据准确。

ORDER BY created_at desc可加可不加,加上只是为了在相同时长下拿到的时长是时间最新的时长。

最终查询结果:

方案3

SELECT t.id, t.name, t.duration maxDuration, t.created_at
FROM (
    SELECT id, name, duration, created_at,
           ROW_NUMBER() OVER (
                    PARTITION BY name ORDER BY duration desc
           ) AS durationFirst
    FROM demo_records 
) t
WHERE t.durationFirst = 1
ORDER BY duration desc

这个语句细心的会发现没有出现group by,那是怎么分组的?答案就是PARTITION BY

查询结果:

区别:

方案3子查询内部必须基于时长来排序(因为最终要编号并获取每个分区内的1号),且最终的ORDER BY duration desc是必要的,因为需求是要按时长字段降序排序,因此最终相同时长的记录不一定是时间最新的记录;方案2兼顾到了相同时长的记录获得时间最新的记录。

对于这个需求来说,上述三个方案均正确,可按实际情况选择。

需求11:基于所有数据找出每人每天的最新一条记录

SELECT  t.id, t.name, t.duration, t.created_at
FROM (
    SELECT id, name, duration, created_at,
           ROW_NUMBER() OVER (
                    PARTITION BY name, DATE(created_at) ORDER BY created_at desc
           ) AS daily_num
    FROM demo_records
) t
WHERE t.daily_num = 1

上述SQL中,DATE函数可直接转换具体时间为天;

详细过程分三步走:

1、PARTITION BY可将数据按名称、天级别时间来进行分区;分区后面的order by表示分区了的数据按创建时间降序排序,便于找出每个分区最新的一条记录。通俗的说就是把每人每天的数据各自单独放在了一起,各自内部已按创建时间降序排序了。

2、ROW_NUMBER()  OVER()窗口函数可对已产生的所有分区的每个记录(每行)分配一个编号,由于每个分区内部已经排好序,编号从1开始,因此最新的那条记录为1。以实际数据来说明,孙悟空有四条数据,其中在3月2日有两条分别是10点、15点,那么孙悟空在3月2日15点的这条记录将被编号为1,10点的那条记录被编号为2。

3、编号完成后,过滤daily_num = 1的即可。(如果要获得最新两天的,则变更where条件daily_num = 2)

结果如下:

符合预期。

 

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

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

相关文章

C++—结构体

结构体&#xff08;struct&#xff09;&#xff0c;是一种用户自定义复合数据类型&#xff0c;可以包含不同类型的不同成员。 结构体的声明定义和使用的基本语法&#xff1a; // 声明结构体struct 结构体类型 { 成员1类型 成员1名称; ...成员N类型 成员N名称; };除声明…

Python导入Shapefile到PostGIS的常见问题和解决方案

导入Shapefile到PostGIS的常见问题和解决方案 先决条件&#xff1a; 已经拥有含有GDAL的python环境&#xff08;如果大家需要&#xff0c;我可以后面出一片文章 问题一&#xff1a;QGIS连接到PostGIS数据库失败 错误描述&#xff1a; Connection to server at &quo…

BCD编码(8421)介绍

概念 BCD (Binary-Coded Decimal) 是一种二进制的数字编码形式&#xff0c;其特点每个十进制数位用4个二进制位来表示。 在网络IO中&#xff0c;你传输一个数字类型最少需要一字节&#xff0c;传输两个数字类型最少需要两字节&#xff0c;但是当你使用BCD编码后传输&#xff…

Oracle Graph 入门 - RDF 知识图谱

Oracle Graph 入门 - RDF 知识图谱 0. 引言1. 查看 RDF Semantic Graph 安装情况2. 创建一个语义网络4. 创建一个模型5. 加载 RDF 文件6. 配置 W3C 标准的 SPARQL 端点 0. 引言 Oracle Graph 的中文资料太少了&#xff0c;只能自己参考英文资料整理一篇吧。 Oracle 数据库包括…

云下到云上,丽迅物流如何实现数据库降本50% | OceanBase案例

在2024年3月20日的首场OceanBase数据库城市行活动中&#xff0c;专注于物流及供应链解决方案的丽迅物流的架构师阳磊&#xff0c;围绕“OB Cloud在丽迅物流的实践”这一主题&#xff0c;进行了精彩的演讲。本文为此次演讲的内容回顾。 在丽迅物流&#xff08;Lesoon Logistics…

论文精读-SRFormer Permuted Self-Attention for Single Image Super-Resolution

论文精读-SRFormer: Permuted Self-Attention for Single Image Super-Resolution SRFormer:用于单图像超分辨率的排列自注意 Params&#xff1a;853K&#xff0c;MACs&#xff1a;236G 优点&#xff1a; 1、参考SwinIR的RSTB提出了新的网络块结构PAB&#xff08;排列自注意力…

盘点28个免费域名申请大全

盘点28个免费域名申请大全 免费域名推荐学习使用&#xff0c;免费就意味着没任何保障。 名称稳定时间支持解析模式后缀格式说明地址EU.org28 年NS.eu.org/. 国家简写.eu.org需要审核&#xff0c;稳定性高&#xff0c;限制少&#xff0c;国内访问有问题&#xff0c;可 CFeu.orgp…

反射获取或修改对象属性的值

利用反射既可以获取也可以写入,首先咱们先写几个获取的例子。 一:利用反射修改各数据(利用resultField.set修改) 首先定义实体类 public class Dog {private String dogUser;private int age;把DogUser的"hahaha"改为"geggegegege" Dog dog = new Do…

10个最佳Android数据恢复工具,用于恢复已删除的文件

由于我们现在在智能手机上存储了许多重要文件&#xff0c;因此了解数据恢复工具变得很重要。您永远不会知道何时需要使用适用于Android的数据恢复工具。 由于不乏Windows数据恢复工具&#xff0c;因此从崩溃的计算机中恢复文件很容易。但是&#xff0c;当涉及到从Android恢复数…

adb卸载系统垃圾应用

//获取包名 输入如下代码&#xff0c;然后在打开和关闭要获取包名的app就会打印出该app的包名 adb shell am monitor //卸载系统应用 -k会保留用户数据&#xff0c;不包含-k则不会保留用户数据 adb shell pm uninstall -k --user 0 包名 &#xff08;包名一般为&#xff1a;c…

机械臂与Realsense D435 相机的手眼标定ROS包

本教程主要介绍机械臂与 Realsense D435 相机手眼标定的配置及方法。 系统&#xff1a;Ubuntu 20.0.4 ◼ ROS&#xff1a;Noetic ◼ OpenCV 库&#xff1a;OpenCV 4.2.0 ◼ Realsense D435&#xff1a;librealsense sdk&#xff08;2.50.0&#xff09;、realsense-ros 功能包&…

【map、set】C++用红黑树来封装map、set容器

&#x1f389;博主首页&#xff1a; 有趣的中国人 &#x1f389;专栏首页&#xff1a; C进阶 &#x1f389;其它专栏&#xff1a; C初阶 | Linux | 初阶数据结构 小伙伴们大家好&#xff0c;本片文章将会讲解map和set之用红黑树来封装map、set容器的相关内容。 如果看到最后您…

资料防拷贝该如何实现?数据防拷贝的方法有哪些

数据安全和隐私保护成为企业和个人关注的重点。电脑中存储的资料往往包含了重要的商业机密、个人隐私或其他敏感信息。 因此&#xff0c;如何有效防止他人非法拷贝电脑资料&#xff0c;成为了一个亟待解决的问题。 本文将探讨数据防拷贝的方法&#xff0c;以帮助企业和个人保护…

22-LINUX--多线程and多进程TCP连接

一.TCP连接基础知识 1.套接字 所谓套接字(Socket)&#xff0c;就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端&#xff0c;提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲&#xff0c;套接字上联应用进程…

Map遍历、反射、GC

map的遍历 用foreach遍历 HashMap<Character,Integer> map new HashMap<>();map.put(A,2);map.put(B,3);map.put(C,3);for (Map.Entry<Character,Integer> entry: map.entrySet()) {char key entry.getKey();int value entry.getValue();System.out.prin…

CF451E: Devu and Flowers(容斥原理 + 考虑反面 + golang组合模版)

题目截图 题目翻译 题目分析 正难则反&#xff0c;考虑所有不符合的例子 由于n很小&#xff0c;所以可以状态压缩二进制遍历完全部不符合例子的组合 对于不符合的例子&#xff0c;假设其中第i个不符合&#xff0c;那么就消耗掉fi 1个球 以此类推&#xff0c;减剩下s2个球 这时…

Android正向开发实现客户端证书认证

前言 如果第三方模块被混淆,那hook方式均不能生效。这时就需要根据系统包去定位校验的函数,因此需要对安卓开发者是如何实现客户端证书校验的有一定了解,接下来就介绍这部分内容。 开发者实现客户端证书校验的本质是:证书/密钥 + 代码。 在形式上有:证书校验、公钥校验和…

Anthropic绘制出了大型语言模型的思维图:大型语言模型到底是如何工作

今天&#xff0c;我们报告了在理解人工智能模型的内部运作方面取得的重大进展。我们已经确定了如何在 Claude Sonnet&#xff08;我们部署的大型语言模型之一&#xff09;中表示数百万个概念。这是对现代生产级大型语言模型的首次详细了解。这种可解释性的发现将来可以帮助我们…

Hadoop 客户端 FileSystem加载过程

如何使用hadoop客户端 public class testCreate {public static void main(String[] args) throws IOException {System.setProperty("HADOOP_USER_NAME", "hdfs");String pathStr "/home/hdp/shanshajia";Path path new Path(pathStr);Confi…

AWS安全性身份和合规性之Artifact

AWS Artifact是对您很重要的与合规性相关的信息的首选中央资源。AWS Artifact是一项服务&#xff0c;提供了一系列用于安全合规的文档、报告和资源&#xff0c;以帮助用户满足其合规性和监管要求。它允许按需访问来自AWS和在AWS Marketplace上销售产品的ISV的安全性和合规性报告…