MaxCompute 挑战使用SQL进行序列数据处理
日常编写数据加工任务,主要的方法就是使用SQL。第一是因为自己对SQL掌握的比较好(十多年数据开发经验,就这几个关键字,也不敢跟别人说自己不行),所以,MR和函数涉及不多。在接触MaxCompute这些年,写过的函数应该不超过10个,主要还是因为自己JAVA水平挫。记得早些年写过一个身份证号码校验函数,当时有个项目反馈一段SQL原来2分钟,使用我的函数就变成12分钟了。当时这个项目组还找到MaxCompute的研发,研发负责人又找到我,让我把我的代码调优下。我很惶恐啊,我是什么渣,我自己心里知道啊。最后还是厚着脸皮求研发帮我优化了下,性能终于改进了。这以后,我更不敢随机作函数了,毕竟MaxCompute官方建议尽可能使用SQL,SQL是优化过的方法,自己用MR和自定义函数性能是很难保障的。这也导致我至今在这方面也是渣渣,当然我认为错不在我,我只是听了“妈妈”的话而已。
最近很奇妙,接连有两个项目遇到了序列值计算的问题,还都是要求不能使用函数和MR。同事把问题送给我,我发现光读懂题都要半天(题目有点绕),不在一线搞开发太久了,有点生疏了。同样的问题,第一次搞了一天,第二次还搞了半天,没说很快能搞出来的,未免有点丢范。所以,总结出来跟大家分享下。
先说下什么是序列值的处理。表中的记录本身是无序的,但是业务上数据都是有序的,一般来说时间就是一个自然的序列。比如利用我一天的作息的时点记录,计算我一天吃了几次饭,吃了多久。乍一看,好像要写个函数。
问题模拟如下:
问题:吃了几次饭,都吃了多久?
条件:1-两个“吃饭”状态间隔在1小时内,算作一顿饭
2-最后一个“吃饭”状态后的下一个其他状态的开始时间,是“吃饭”的结束时间
通过上面的分析,我们可以得到结果:大约吃了四次饭,因为晚上吃饭的时间很长,按照规则算作吃了两次饭(第四次看起来是去撸串了)。我是怎么做的呢?第一步,我先把无关的信息剔除了,第1行、第4行、最后1行。第二步,后我利用数据是连续的时间的特质,找到了状态的结束时间。第三步,我识别了状态间隔1小时这个特征,识别出了一个“吃饭”中混杂的其他无关状态,并且还分析得到第三个“吃饭”和第四个“吃饭”状态是两个独立的状态。
那么用SQL怎么实现?排序是一定的了,要排序还要处理状态,必须使用窗口函数。能选的窗口函数似乎只有lag、lead。
窗口函数:
LAG 按偏移量取当前行之前第几行的值。
LEAD 按偏移量取当前行之后第几行的值。
官方文档:https://help.aliyun.com/document_detail/34994.html
即便有了这个函数,还有一个问题很头疼,函数需要指定偏移量,而这个问题里面并不知道到底会出现多少个状态。是不是也没有用呢?看看再说。
问题分解分解如下:
使用LAG\LEAD函数取到前一条记录和后一条记录的状态和时间,分析记录:
1-当前状态不是“吃饭”,上一个状态也不是“吃饭”,记录不保留。
2-当前状态不是“吃饭”,上一个状态是“吃饭”,为上一个状态提供结束时间,记录不保留。
3-当前状态是“吃饭”,记录上一个和下一个状态都是“吃饭”,记录不保留。
4-当前状态是“吃饭”,记录下一个状态时间,作为当前状态结束时间,记录保留。
如下图:
然后我们就得到了下面一个表格:
很明显,这不是我们最后需要的。虽然我们找到了状态为“吃饭”的行,并且通过窗口函数给它找到了状态的结束实际。但是表格还需要再作一次处理,才能变成我们想要的结果。再次使用LAG\LEAD函数,我们需要把间隔在1小时内的“吃饭”状态进行合并。
问题再次分解分解如下:
使用LAG\LEAD函数取到前一条记录和后一条记录的开始和结束时间,分析记录:
1-当前记录的“开始时间”减去上个时点的“结束时间”,如果小于1小时,该行记录不保留。这一行记录的状态需要与上一行合并为一次“吃饭”状态。下图中绿色标注行。
2-下个时点的“开始时间”减去当前记录的“结束时间”,如果小于1小时,该行记录与下一行记录合并。修改当前时点“吃饭”状态的结束时间为下一个时点的结束时间。下图橙色标注行。
然后我们得到了下面的表格:
不管之前我们想的多复杂,需要用什么循环或者递归逻辑实现,但是现在问题解决了。我们通过这个表格回答了最开始题目的问题。这个人吃过4次饭,开始时间分别是7点10分、12点25分、17点40分、19点45分,每次持续的时间大约都在1小时。这个过程就是一个找到需要的信息,剔除无关信息的过程,只不过这个where有点复杂。
其实从分析问题的角度来看,这个问题本身就有点复杂,搞懂问题一般都需要一定的时间。从实现问题的角度来看,使用高级语言JAVA或者python实现更容易点,循环撸一遍有什么解决不了的(一遍不够再来一遍)。用SQL实现,看起来有点复杂(可能是我常年使用SQL语言的原因,我觉得我好像分析问题的过程跟实现的过程是一样的。),但是代码量一定是最少的(性能可能也是最佳的)。再从可维护性上去综合比较,还是使用SQL实现更优。
所以,后面再遇到类似的问题,你应该可以搞定了。如果有点困难,至少你可以再回过头来看下这个例子,毕竟我花了好久来设计。
SQL问题解答:
with ta as(
select*
from values
(1001,'06:05:00','sleep')
,(1001,'07:10:00','eat')
,(1001,'08:15:00','phone')
,(1001,'11:20:00','phone')
,(1001,'12:25:00','eat')
,(1001,'12:40:00','phone')
,(1001,'13:30:00','eat')
,(1001,'13:35:00','sleep')
,(1001,'17:40:00','eat')
,(1001,'18:05:00','eat')
,(1001,'18:25:00','eat')
,(1001,'18:30:00','phone')
,(1001,'19:45:00','eat')
,(1001,'20:55:00','phone')
,(1001,'22:00:00','sleep')
t(id,stime,stat))
-- 5 计算根据前后记录的时间,判断记录是否要被合并
selectid,stime
,case whens2<=60 thenetime2 else etime end asetime,stat
from(
-- 4 计算前后记录的时间差
selectid,stime,etime,stat
,datediff(stime,etime1,'mi') ass1
,datediff(stime2,etime,'mi') ass2
,etime2
from(
-- 3 计算前后记录的时间
selectid,stime,etime,stat
,lag (stime,1) over(partition byid order by stime asc)as stime1
,lag (etime,1) over(partition byid order by stime asc)as etime1
,lead(stime,1) over(partition byid order by stime asc)as stime2
,lead(etime,1) over(partition byid order by stime asc)as etime2
from(
-- 2 识别前后记录状态,找到状态结束时间
selectid,stime,stat
,lead(stime,1) over(partition byid order by stime asc)as etime
,lag (stat,1) over(partition byid order by stime asc)as stat1
,lead(stat,1) over(partition byid order by stime asc)as stat2
from(
-- 1 把字符串转时间
selectid,to_date(concat('2021-06-29 ',stime),'yyyy-mm-dd hh:mi:ss') asstime,stat
fromta)t1)t2
wherestat='eat' and not(stat='eat' andstat1='eat' andstat2='eat'))t3)t4
wheres1 >60 ors1 is null
;
原文链接
本文为阿里云原创内容,未经允许不得转载。
MaxCompute 挑战使用SQL进行序列数据处理的更多相关文章
- SQL Server 序列(SEQUENCE)使用
众所周知,在之前的SQL SERVER版本中,一般采用GUID或者IDENTITY来作为标示符,但是IDENTITY是一个表对象,只能保证在一张表里面的序列,当我们遇到以下情况时, 如上表,我们需要在 ...
- SQL 使用序列
SQL 使用序列 序列是根据需要产生的一组有序整数:1, 2, 3 ... 序列在数据库中经常用到,因为许多应用要求数据表中的的每一行都有一个唯一的值,序列为此提供了一种简单的方法. 本节阐述在 My ...
- ORACLE PL/SQL 中序列(sequence)的简易使用方法介绍
如果我是C罗 原文 ORACLE PL/SQL 中序列(sequence)的简易使用方法介绍 sequence在ORACLE中应用十分广泛,就是序列号的意思,会自动增加指定变数,如逐次增加1或者2或者 ...
- SQL基础--序列
序列是一种数据库对象,用来自动产生一组唯一的序号:序列是一种共享式的对象,多个用户可以共同使用序列中的序号. 序列的创建语法 CREATE SEQUENCE sequencename [INCREME ...
- Spark SQL结构化数据处理
Spark SQL是Spark框架的重要组成部分, 主要用于结构化数据处理和对Spark数据执行类SQL的查询. DataFrame是一个分布式的,按照命名列的形式组织的数据集合. 一张SQL数据表可 ...
- 一探究竟:善用 MaxCompute Studio 分析 SQL 作业
头疼的问题 MaxCompute 用户一个常见的问题是:同一个周期任务,为什么最近几天比之前慢了很多?或者为什么之前都能按时产出的作业最近经常破线? 通常来说,引起作业执行变慢的原因有:quota 组 ...
- MaxCompute如何对SQL查询结果实现分页获取
由于MaxCompute SQL本身不提供类似数据库的select * from table limit x offset y的分页查询逻辑.但是有很多用户希望在一定场景下能够使用获取类似数据库分页的 ...
- sql server 大数据处理
对SQL Server数据表进行分区的过程分为三个步骤: 1)建立分区函数 2)建立分区方案 3)对表格进行分区 第一个步骤:建立分区函数 分区函数定义[u]how[/u],即你想要SQL Serve ...
- Oracle SQL之 序列使用限制
Restrictions on Sequence Values You cannot use CURRVAL and NEXTVAL in thefollowing constructs:■ A su ...
- Mybatis 通过动态SQL获取序列值
配置文件 <select id="getSeq" parameterType="string" resultType="long" ...
随机推荐
- NJOPT自控第三次积分赛--风力摆小结
NJOPT自控第三次积分赛--风力摆小结 题目 题目就不放了,百度一搜就有,就是2015国赛的风力摆.. 方案 我们队采用的主控是STM32F401CCU6(科协传统),性能完全够用:姿态传感器采用的 ...
- Redis(5)——亿级数据过滤和布隆过滤器
一.布隆过滤器简介 上一次 我们学会了使用 HyperLogLog 来对大数据进行一个估算,它非常有价值,可以解决很多精确度不高的统计需求.但是如果我们想知道某一个值是不是已经在 HyperLogLo ...
- Linux Mint下Qt Creator无法输入中文解决办法
注,本文所指的是linux中使用fcitx输入框架下,Qt程序输入中文的解决办法 如果是ibus输入框架,则不需要任何操作,可以直接输入中文 但是微信使用的是fcitx输入框架,且比较常用,故只能使用 ...
- KingbaseES错误分析 -- “requested character too large”
一.适用于: 本文档使用于KingbaseES所有版本. 二.问题现象: 使用从其他数据库迁移到KingbaseES数据库的自定义函数.存储过程.Package包..出现以下错误信息: 错误:所请求的 ...
- Java日期、字符串、毫秒值格式转换
1 /** 2 * 3 */ 4 package study.reliable; 5 /** 6 * @author : Administrator 7 * @date :2022年4月21日 下午9 ...
- BeautifulSoup 库 和 re 库 解析腾讯视频电影
1 import requests 2 import json 3 from bs4 import BeautifulSoup #网页解析获取数据 4 import sys 5 import re 6 ...
- python 1992和2006年国家标准学科分类和代码标准化并存入MySQL数据库
数据表 代码 1 import pandas as pd 2 import pymysql 3 4 5 def get_subject_1992(): 6 res={} 7 the_former_co ...
- 【已解决】linux centos7系统磁盘扩容
第一步要手动加硬盘(我的操作是在20G的基础上加了30G) [reliable@hadoop102 ~]$ su root密码: 查看当前磁盘挂载情况: [root@hadoop102 reliabl ...
- 3个.NET开源、免费、强大的商城系统
前言 今天大姚给大家分享3个.NET开源.免费.强大的商城系统,希望可以帮助到有商城系统开发需求的同学. nopCommerce nopCommerce是一个功能丰富.免费.灵活且可定制的开源电子商务 ...
- #分块,懒标记#LOJ 3631「2021 集训队互测」学姐买瓜
题目传送门 分析 有一个很简单的做法就是处理出每个位置能够一次到达的最左边的右端点(后继). 然后直接从 \(l\) 开始能跳就跳,这样单次询问时间复杂度是 \(O(n)\) 的. 观察到时间复杂度因 ...