数据蒋堂 | 有序分组

我们知道,SQL延用了数学上的无序集合概念,所以SQL的分组并不关注过待分组集合中成员的次序。我们在前面讨论过的等值分组和非等值分组,也都没有关注过这个问题,分组规则都是建立在本身的成员取值本身上。但如果我们要拓展SQL,以有序集合为考虑对象时,那就必须考虑成员次序对分组的影响了,而且,现实业务中有大量的有序分组应用场景。

一个简单的例子:将一个班的学生平均分成三份(假定人数能被3整除)。按我们在前面所说的分组定义,这也可以看成是一种分组,但这个运算在SQL中却很难写出来,因为分组依据和成员取值没有关系。

如果使用我们在前面讲有序遍历语法时的#符号,这个问题就很容易解决了。

用SQL实现这个运算就麻烦很多,需要先用子查询造出一个序号,然后再执行类似的分组规则。

上面这个例子中其实还没有真正关注成员的次序,只是说明了序号的作用,当待分组集合的成员是其它次序时也可以得到可用的结果。

我们再看更多例子。

处理文本日志时,有些日志的基本单位不是1行,而可能是3行,即每个事件总是写出3行文本,这并不是多罕见的情况。对付这种日志时,就需要把文本每3行拆成一个分组子集,然后针对每个分组再进行详细的分析处理。这时要正确的分组运算就必须依赖于待分组集合中成员(文本日志的行)的次序了。

入学考试之后,把学生按成绩排序蛇行分拆成两个班,即名次1,4,5,8,...在一个班,而2,3,6,7,...在另一个班,这样能保证两个班的平均名次是相同的。这个分组也可以用序号做出来:

这里用的分组值不再是常见的普通数值,而是一个布尔量,相当于按“真“值和“假”值分成两个组,真值对应第一个班,假值对应另一个班。本质上讲,这还是个等值分组,只是用到的分组值可以是任意泛型。

显然,这个分组的正确性也严重依赖于待分组集合的成员次序。

顺便说一句,这又是一个只关注分组子集而不关心聚合值的例子。

按序号分组在很多情况下就是用序号来计算出分组依据,然后就变成普通的等值分组了。那么有没有不能简单地转换成等值分组的情况呢?

有一组婴儿出生记录,是按出生次序排序的,我们现在关心连续出生的同性别婴儿数量超过5的有多少批?

简单想,这就是先GROUP,计算每组COUNT值,然后数出有几个大于5的。后两步很简单,问题是怎么GROUP?

直接按婴儿性别分组当然是不对的,必须考虑次序,依次扫描记录,当婴儿性别发生变化时则产生一个新组。这种分组显然没法直接用等值分组做出来了。

我们可以提供一个有序分组方法来实现这种分组:当考察值发生变化时就产生一个新的分组。

用SQL就麻烦很多,需要先造成中间标志和变量来生成组的序号,大概是这样

这样的SQL,看懂都不是很容易的。而且必须借助birthday这种字段来形成次序,而前述的有序分组写法在原数据有序时根本用不着这个信息。

这种场景同样可能出现在文本分析中。每个用户的事件日志可能有多行,而且行数不确定,但写日志时会在每个行开始处写上用户号。这样我们可以按这个用户号进行有序分组,它变化时就说明是另一个用户的事件了。

即使是普通的等值分组,如果事先知道原集合对分组字段有序,也可以使用这种方案来实施,这将获得更高的性能,比数据库常用的HASH分组方案要快得多,而且特别适合大数据遍历的情况。

再看一个著名的问题:一支股票最长连续上涨了多少天?

这个问题当然可以直接遍历去解决,不过我们现在用分组的思路来处理,至少在SQL体系下只能这么做(严格些说,这是目前找到的最简单可行的办法)。

将股票收盘价按日期排序,然后将连续上涨的日期分到同一组,这样只要考虑哪一组成员数最多即可。更明确地说,就是当某天上涨了,就把这一天和前一天分到一个组中,某天下跌了,则产生一个新组。

用SQL实现这个思路,同样需要用中间标志和变量来生成组序号:

如果有专门的有序分组方法以及以前说过的有序遍历语法,这个运算就很简单了:

与SQL不同,虽然实现思路完全一样,但写出来是分步的,而不是一个多层嵌套语句,并且书写和理解都要容易得多。

同样地,这种场景也会在文本分析中有用。不确定行数的日志中,有时会在事件开始时写一个标志串,当扫描到这个标志串的时候就产生一个新的分组,有序分析的条件可设定为当前扫描行和指定文字相同,这样就能保证同一事件的日志信息在同一个组中。

后两种有序分组的情况,理论上当然也可以转换成等值分组来处理(用SQL就要这么做,这也能从另一个侧面说明SQL运算体系的完备性),但确实是相当麻烦的,所以我们一般不把它再当成等值分组来处理了。

到目前为止的分组讨论,都是假定待分组集合已经准备好,其成员可以被随机访问到。但假设数据量巨大而不能全部读入时,如果继续做这种假定,会导致频繁的外存交换而性能极差,这时需要再设计以流方式边读入边分组并且边聚合的运算体系。事实上日志分析中更常见的是这种情况,这些问题我们将再撰文研究,但基本方法思路仍然离不开上面这些内容。

清华大学计算机硕士,著有《非线性报表模型原理》等,1989年,中国首个国际奥林匹克数学竞赛团体冠军成员,个人金牌;2000年,创立润乾公司;2004年,首次在润乾报表中提出非线性报表模型,完美解决了中国式复杂报表制表难题,目前该模型已经成为报表行业的标准;2014年,经过7年开发,润乾软件发布不依赖关系代数模型的计算引擎——集算器,有效地提高了复杂结构化大数据计算的开发和运算效率;2015年,润乾软件被福布斯中文网站评为“2015福布斯中国非上市潜力企业100强”;2016年,荣获中国电子信息产业发展研究院评选的“2016年中国软件和信息服务业十大领军人物”;2017年, 自主创新研发新一代的数据仓库、云数据库等产品即将面世。

《数据蒋堂》的作者蒋步星,从事信息系统建设和数据处理长达20多年的时间。他丰富的工程经验与深厚的理论功底相互融合、创新思想与传统观念的相互碰撞,虚拟与现实的相互交织,产生出了一篇篇的沥血之作。此连载的内容涉及从数据呈现、采集到加工计算再到存储以及挖掘等各个方面。大可观数据世界之远景、小可看技术疑难之细节。针对数据领域一些技术难点,站在研发人员的角度从浅入深,进行全方位、360度无死角深度剖析;对于一些业内观点,站在技术人员角度阐述自己的思考和理解。蒋步星还会对大数据的发展,站在业内专家角度给予预测和推断。静下心来认真研读你会发现,《数据蒋堂》的文章,有的会让用户避免重复前人走过的弯路,有的会让攻城狮面对扎心的难题茅塞顿开,有的会为初入行业的读者提供一把开启数据世界的钥匙,有的甚至会让业内专家大跌眼镜,产生思想交锋。

时间: 2024-09-16 04:33:10

数据蒋堂 | 有序分组的相关文章

数据蒋堂 | 还原分组运算的本意

分组是SQL中常见的运算,但未必所有人都能深刻地理解它. 分组运算的实质是将一个集合按照某种规则拆分成若干个子集,也就是说,返回值应当是一个由集合构成的集合,但人们一般不太关心构成这个集合的成员集合(我们称为分组子集),而是对这些子集的聚合值更感兴趣,因此,分组运算常常伴随着对子集的进一步汇总计算. SQL就是这么做的,在写有GROUP BY子句时,SELECT部分除了分组字段外,就只能写入聚合运算表达式了.当然还有个原因是SQL没有显式的集合数据类型,无法返回集合的集合这类数据,也只能强迫实施

数据蒋堂 | 非等值分组

我们在上一期研究了分组运算的实质,即将一个集合按某种规则拆分成若干子集.不过,上期的关注重点在于还原分组运算的步骤,而没有讨论拆分规则,例子中都是用某些字段(或表达式)来定义拆分规则,也就是SQL中使用的方法. 我们把这种拆分方式称为等值分组. 等值分组在数学上的描述,相当于在一个集合上定义了一个等价关系:分组字段(表达式)相等的成员(记录)就认为等价. 等价关系是指满足如下条件的关系: 1)交换性,若a=b则b=a 2)传递性,若a=b,b=c则a=c 3)排他性,对任何a,b,a=b和a!=

【数据蒋堂】第27期:非常规聚合

标准SQL中提供了五种最常用的聚合运算:SUM/COUNT/AVG/MIN/MAX.观察这几个运算,我们发现它们都可以看成是一个以集合为参数返回单值的函数,我们就先把这个共同点理解为聚合运算的定义,把集合变成单值,多个值变成一个值,也就是发生了"聚合",所以叫聚合运算. 那么很显然,有集合的时候就可以应用聚合运算了,所以SUM/COUNT这些运算可以针对一个数据表(记录集合)实施. 分组运算的结果是一批分组子集,那么每个子集上也可以应用聚合运算,这也就是SQL的分组运算了.其实针对全集

数据蒋堂 | 从SQL语法看离散性

所谓离散性,是指集合的成员可以游离在集合之外存在并参与运算,游离成员还可以再组成新的集合.从离散性的解释上可以知道,离散性是针对集合而言的一种能力,离开集合概念单独谈离散性就没有意义了. 离散性是个很简单的特性,几乎所有支持结构(对象)的高级语言都天然支持,比如我们用Java时都可以把数组成员取出来单独计算,也可以再次组成新的数组进行集合运算(不过Java几乎没有提供集合运算类库). 但是SQL的离散性却很差. SQL体系中有记录的概念,但并没有显式的记录数据类型.单条记录被SQL作为只有一条记

【数据蒋堂】第20期:从SQL语法看离散性

所谓离散性,是指集合的成员可以游离在集合之外存在并参与运算,游离成员还可以再组成新的集合.从离散性的解释上可以知道,离散性是针对集合而言的一种能力,离开集合概念单独谈离散性就没有意义了. 离散性是个很简单的特性,几乎所有支持结构(对象)的高级语言都天然支持,比如我们用Java时都可以把数组成员取出来单独计算,也可以再次组成新的数组进行集合运算(不过Java几乎没有提供集合运算类库). 但是SQL的离散性却很差. SQL体系中有记录的概念,但并没有显式的记录数据类型.单条记录被SQL作为只有一条记

【数据蒋堂】第17期:SQL的困难源于关系代数

在结构化数据处理领域,SQL无疑是应用最广泛的工作语言,不仅被所有关系数据库采用,许多新进的大数据平台也将实现SQL作为目标.但现实是,面对当前纷杂的计算查询需求,SQL在很多方面并不够好用.我们在前面说过SQL的过程性问题,这其实并不是最关键的问题,SQL的更大困难来源于其理论基础,即关系代数. 关系代数是一种代数体系.我们无法在本文的篇幅中严格定义代数体系这个概念,只能通俗地解释.人们为解决某种运算问题,定义了一些数据对象及针对这些数据对象的一套运算规则,确保这些运算的封闭性和自洽性,就可以

数据蒋堂 | SQL的困难源于关系代数

在结构化数据处理领域,SQL无疑是应用最广泛的工作语言,不仅被所有关系数据库采用,许多新进的大数据平台也将实现SQL作为目标.但现实是,面对当前纷杂的计算查询需求,SQL在很多方面并不够好用.我们在前面说过SQL的过程性问题,这其实并不是最关键的问题,SQL的更大困难来源于其理论基础,即关系代数. 关系代数是一种代数体系.我们无法在本文的篇幅中严格定义代数体系这个概念,只能通俗地解释.人们为解决某种运算问题,定义了一些数据对象及针对这些数据对象的一套运算规则,确保这些运算的封闭性和自洽性,就可以

【数据蒋堂】功夫都在报表外--漫谈报表性能优化

应用系统中的报表,作为面向业务用户的窗口,其性能一直被高度关注.用户输入参数后都希望立即就能看到统计查询结果,等个十几二十秒还能接受,等到三五分钟的用户体验就非常恶劣了. 那么,报表为什么会慢,又应当从哪里入手进行性能调优呢? 数据准备 当前应用中的报表大都用报表工具开发,当报表响应太慢时,不明就里的用户就会把矛头指向使用报表工具的开发人员或者报表工具厂商.其实,大多数情况报表的慢只是个表现,背后的原因是数据准备太慢,在数据进入报表环节之前就已经慢了,这时再去优化报表开发或压迫报表工具并没有用处

【数据蒋堂】第28期:迭代聚合语法

我们讨论过的常规聚合运算如SUM/COUNT和非常规聚合运算如maxp/top,都是事先设计好的聚合函数.但如果我们想实现一个以前没有定义过的运算怎么办?是否可以用已有的语法和函数组合出来?比如想做连乘运算,显然这也算是一种聚合. 要设计这样的语法方案,我们来看看这些聚合结果值是如何被程序计算出来的. SUM:先设置一个初始值0,然后遍历集合的每个成员,每次将成员值加到初始值上,直到成员被遍历完.COUNT:设置初始值0,遍历集合成员,每次碰到非空成员将初始值加1,直到遍历完.AVERAGE:这