面向OLAP的列式存储DBMS-8-[ClickHouse]的常用聚合函数

news2024/10/7 20:36:03

ClickHouse 中的常用聚合函数
在这里插入图片描述

1 聚合函数

ClickHouse 中的聚合函数,因为和关系型数据库的相似性,本来聚合函数不打算说的,但是 ClickHouse 提供了很多关系型数据库中没有的函数,所以我们还是从头了解一下。

1.1 count

count:计算数据的行数,有以下几种方式:

count(字段):计算该字段中不为 Null 的元素数量
count()、count(*):计算数据集的总行数

一、所以如果某个字段中不包含 Null,那么对该字段进行 count 得到的结果和 count()、count(*) 是相等的。

SELECT count(), count(*), count(product) FROM sales_data;

二、这里再提一下聚合函数,聚合函数针对的是多行结果集,而不是数组。

-- 这里得到的是 1,原因在于这里只有一行数据
SELECT count([1, 2, 3]);
/*
┌─count()─┐
│       1 │
└─────────┘
*/

-- 如果将其展开的话,那么会得到 3,因为展开之后变成了 3 行数据
SELECT count(arrayJoin([1, 2, 3]));
/*
┌─count(arrayJoin([1, 2, 3]))─┐
│                           3 │
└─────────────────────────────┘
*/

三、当然使用 count 计算某个字段的元素数量时,还可以进行去重。

SELECT count(DISTINCT product) FROM sales_data;
/*
┌─uniqExact(product)─┐
│                  3 │
└────────────────────┘
*/

-- 根据返回的字段名,我们发现 ClickHouse 在底层实际上调用的是 uniqExact 函数
SELECT uniqExact(product) FROM sales_data;
/*
┌─uniqExact(product)─┐
│                  3 │
└────────────────────┘
*/
-- 也就是 count(DISTINCT) 等价于 uniqExact
-- 不过还是建议像关系型数据库那样使用 count(DISTINCT) 比较好,因为更加习惯

1.2 sum

min、max、sum、avg:计算每组数据的最小值、最大值、总和、平均值

SELECT min(amount), max(amount), sum(amount), avg(amount) 
FROM sales_data GROUP BY product, channel;

1.3 least和greatest

除此之外还有两个非聚合函数 least、greatest 也比较实用,那么这两个函数是干什么的呢?看一张图就明白了。

SELECT least(A, B), greatest(A, B) FROM test_1;

在这里插入图片描述
问题来了,如果 ClickHouse 没有提供 least 和 greatest 这两个函数,那么我们要如何实现此功能呢?首先我们可以使用 arrayMap:

-- 由于 arrayMap 针对的是数组,不是多行结果集,所以需要借助 groupArray 将多行结果集转成数组
-- 另外在比较大小的时候也要将两个元素组合成数组 [x, y],然后使用 arrayMin 比较
-- 或者使用 least(x, y) 也可以对两个标量进行比较,不过这里我们是为了实现 least,所以就不用它了
SELECT arrayMap(x, y -> arrayMin([x, y]), groupArray(A), groupArray(B)) arr FROM test_1;
/*
┌─arr───────────┐
│ [11,7,5,11,9] │
└───────────────┘
*/

-- 结果确实实现了,但结果是数组,我们还要再将其展开成多行
-- 这里我们使用 WITH,注意 WITH 子句里的查询只可以返回一行结果集
WITH (
    SELECT arrayMap(x, y -> arrayMin([x, y]), groupArray(A), groupArray(B)) FROM test_1
) AS arr SELECT arrayJoin(arr);
/*
┌─arrayJoin(arr)─┐
│             11 │
│              7 │
│              5 │
│             11 │
│              9 │
└────────────────┘
*/

以上就实现了 least,至于 greatest 也是同理。那么除了使用数组的方式,还可以怎么做呢?如果将这个问题的背景再改成关系型数据库的话,你一定能想到,没错,就是 CASE WHEN。

SELECT CASE WHEN A < B THEN A ELSE B END FROM test_1;
/*
┌─multiIf(less(A, B), A, B)─┐
│                        11 │
│                         7 │
│                         5 │
│                        11 │
│                         9 │
└───────────────────────────┘
*/

整个过程显然变得简单了,所以也不要忘记关系型数据库的语法在 ClickHouse 中也是可以使用的,另外我们看到返回的结果集的字段名叫 multiIf…,虽然我们使用的是 CASE WHEN,但是 ClickHouse 在底层会给语句进行优化,在功能不变的前提下,寻找一个在 ClickHouse 中效率更高的替代方案。因此你直接使用 multiIf… 也是可以的,比如:

SELECT multiIf(less(A, B), A, B) FROM test_1

而至于上面的 multiIf,它的功能和 CASE WHEN 是完全类似的。只不过这里个人有一点建议,既然 ClickHouse 会进行语句的优化,那么能用关系型数据库语法解决的问题,就用关系型数据库语法去解决。这么做的原因主要是为了考虑 SQL 语句的可读性,因为相比 ClickHouse,大部分人对关系型数据库语法显然更熟悉一些。如果使用这里的 mulitIf…,那么当别人阅读时,可能还要查阅一下 multiIf 函数、或者 mulitIf 里面又调用的 less 函数是做什么的;但如果使用 CASE WHEN,绝对的一目了然。

1.4 any

any:选择每组数据中第一个出现的值

-- 按照product, channel进行分组之后,我们可以求每组的最小值、最大值、平均值等等
-- 而这里的 any 则表示获取每组第一个出现的值
SELECT any(amount) FROM sales_data GROUP BY product, channel;

当然 any 看起来貌似没有实际的意义,因为聚合之后每组第一个出现的值并不一定能代表什么。

一、那么问题来了,如果想选择分组中的任意一个值,该怎么办呢?

-- 使用 groupArray 变成一个数组,然后再通过索引选择即可
-- 因为我们选择的是第 1 个元素,所以此时等价于 any
SELECT groupArray(amount)[1] FROM sales_data 
GROUP BY product, channel;

二、如果想分组之后选择,选择每个组的最小值该怎么做呢?

-- 在上面的基础上再调用一下 arrayMin 即可
SELECT arrayMin(groupArray(amount)) FROM sales_data
GROUP BY product, channel;

三、如果想分组之后选择,选择每个组的第 N 大的值该怎么做呢?比如我们选择第 3 大的值。

-- 从小到大排个序即可,然后选择索引为 -3 的元素
-- 或者从大到小排个序,然后选择索引为 3 的元素
SELECT arraySort(groupArray(amount))[-2] rank3_1, arrayReverseSort(groupArray(amount))[2] rank3_2
FROM sales_data GROUP BY product, channel;
/*
┌─rank3_1─┬─rank3_2─┐
│    27842784 │
│    28042804 │
│    26502650 │
│    28562856 │
│    28652865 │
│    26102610 │
│    26322632 │
│    27542754 │
│    26942694 │
└─────────┴─────────┘
*/

确实给人一种 pandas 的感觉,之前做数据分析主要用 pandas。但是 pandas 有一个致命的问题,就是它要求数据能全部加载到内存中,所以在处理中小型数据集的时候确实很方便,但是对于大型数据集就无能为力了,只能另辟蹊径。但是 ClickHouse 则是通过分片机制支持分布式运算,所以个人觉得它简直就是分布式的 pandas。

1.5 varPop方差

四、varPop:计算方差;stddevPop:计算标准差,等于方差开根号

SELECT varPop(amount) v1, stddevPop(amount) v2, v2 * v2 
FROM sales_data;

问题来了,如果我们想手动实现方差的计算该怎么办?试一下:

-- 将结果集转成数组,并先计算好平均值
WITH (SELECT groupArray(amount) FROM sales_data) AS arr, 
      arraySum(arr) / length(arr) AS amount_avg
-- 通过arrayMap将数组中的每一个元素都和平均值做减法,然后再平方,得到新数组
-- 最后再用arrayAvg对新数组取平均值,即可计算出方差
SELECT arrayAvg(
    arrayMap(x -> pow(x - amount_avg, 2), arr)
) 

1.6 covarPop协方差

五、covarPop:计算协方差

比较少用,这里不演示的了,可以自己测试一下。

1.7 anyHeavy

anyHeavy:使用 heavy hitters 算法选择每组中出现频率最高的值

SELECT anyHeavy(amount) FROM sales_data;

1.8 anyLast

anyLast:选择每组中的最后一个值

SELECT anyLast(amount) FROM sales_data GROUP BY product, channel;

-- 同样可以借助数组实现
SELECT groupArray(amount)[-1] FROM sales_data 
GROUP BY product, channel;

1.9 argMin和argMax

argMin:接收两个列,根据另一个列选择当前列的最小值,我们画一张图,通过和 min 进行比对,就能看出它的用法了
在这里插入图片描述
首先 min(A) 和 min(B) 分别返回 5 和 7 无需解释,而 argMin(A, B) 表示根据 B 的最小值选择 A,B 的最小值是 7,对应 A 就是 8;同理 argMin(B, A) 表示根据 A 的最小值选择 B,A 的最小值是 5,对应 B 就是 8。

以上就是 argMin,同理还有 argMax。

1.10 topK

topK:选择出现频率最高的 K 个元素

-- 这里选择出现频率最高的两个元素
SELECT topK(2)(arrayJoin([1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3]));
/*
┌─topK(2)(arrayJoin([1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3]))─┐
│ [1,3]                                                    │
└──────────────────────────────────────────────────────────┘
*/

我们看到以数组的形式返回,因为聚合函数最终每个组只会对应一行数据,所以得到的是数组。

topK 也是非常常见的,如果让我们自己实现,虽然可以做到,但会比较麻烦,ClickHouse 替我们考虑的还是很周到的。

1.11 groupArrayMovingSum

groupArrayMovingSum:滑动窗口,每个窗口内的数据进行累和。

SELECT groupArray(number), groupArrayMovingSum(4)(number)
FROM (SELECT number FROM numbers(10));
/*
┌─groupArray(number)────┬─groupArrayMovingSum(4)(number)─┐
│ [0,1,2,3,4,5,6,7,8,9][0,1,3,6,10,14,18,22,26,30]    │
└───────────────────────┴────────────────────────────────┘
*/

画一张图,来解释一下:首先 groupArrayMovingSum(4) 表示窗口的长度为 4,然后不断的向下滑动,计算包含当前元素在内往上的四个元素之和。如果元素的个数不够窗口的长度,那么有几个算几个,比如前三个元素。
在这里插入图片描述
那么试想一下,如果窗口长度等于数组的长度,那么会发生什么呢?

-- 不指定窗口长度,那么窗口长度就等于数组长度
SELECT groupArray(number), groupArrayMovingSum(number)
FROM (SELECT number FROM numbers(10));
/*
┌─groupArray(number)────┬─groupArrayMovingSum(number)─┐
│ [0,1,2,3,4,5,6,7,8,9][0,1,3,6,10,15,21,28,36,45] │
└───────────────────────┴─────────────────────────────┘
*/
-- 显然相当于进行了累和

这就是 ClickHouse 提供的窗口函数,但关系型数据库中的窗口函数语法在 ClickHouse 还没有得到完美的支持,但很明显通过这些强大的函数我们也可以实现相应的功能。

除了 groupArrayMovingSum 之外,还有一个 groupArrayMovingAvg,用法完全一样,只不过计算的是平均值,这里就不单独说了。

1.12 groupArraySample

groupArraySample:随机选择 N 个元素

-- 随机选择 3 个元素
SELECT groupArraySample(3)(amount) FROM sales_data;
/*
┌─groupArraySample(3)(amount)─┐
│ [1268,2246,1606]            │
└─────────────────────────────┘
*/

我们还可以绑定一个随机种子,如果种子一样,那么每次随机选择的数据也是一样的。

SELECT groupArraySample(3, 666)(amount) FROM sales_data;
/*
┌─groupArraySample(3, 666)(amount)─┐
│ [635,1290,1846]                  │
└──────────────────────────────────┘
*/

SELECT groupArraySample(3, 666)(amount) FROM sales_data;
/*
┌─groupArraySample(3, 666)(amount)─┐
│ [635,1290,1846]                  │
└──────────────────────────────────┘
*/

SELECT groupArraySample(3, 661)(amount) FROM sales_data;
/*
┌─groupArraySample(3, 661)(amount)─┐
│ [2011,2125,1542]                 │
└──────────────────────────────────┘
*/

1.13 deltaSum

deltaSum:对相邻的行进行做差,然后求和,注意:小于 0 不会计算在内

-- 3 - 1 = 2
-- 4 - 3 = 1
-- 1 - 4 = -3
-- 8 - 1 = 7
-- 所以结果为 2 + 1 + 7
SELECT deltaSum(arrayJoin([1, 3, 4, 1, 8]));
/*
┌─deltaSum(arrayJoin([1, 3, 4, 1, 8]))─┐
│                                   10 │
└──────────────────────────────────────┘
*/

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue3 用src动态引入本地图片

&#x1f4ad;&#x1f4ad; ✨&#xff1a; Vue3 用src动态引入本地图片   &#x1f49f;&#xff1a;东非不开森的主页   &#x1f49c;: 躲起来的星星也在努力发光 你也要&#x1f49c;&#x1f49c;   &#x1f338;: 如有错误或不足之处&#xff0c;希望可以指正&#…

Qt OpenGL(二十二)——Qt OpenGL 核心模式-VAO和VBO

Qt OpenGL(二十二)——Qt OpenGL 核心模式-VAO和VBO 一、再谈VAO、VBO 上一篇文章,通过VAO、VBO绘制了一个三角形,过程需要创建VAO、VBO和释放。之所以有这些步骤,就是因为OpenGL本质就是一个大的状态机。但是我们如果要继续学习核心模式的OpenGL的话,VAO、VBO是我们必…

Java集合(一):泛型与Collection集合

目录 集合预热&#xff1a;泛型 泛型的优点 自定义泛型类型 自定义泛型类/接口 泛型使用细节 自定义泛型方法 泛型与继承关系 不存在继承关系的情况 通配符与存在继承关系的情况 泛型受限 集合概述 集合的作用与存储内容 集合与数据结构 集合&#xff1a;Collectio…

【基础算法系列】离散化与前缀和算法的运用

⭐️前面的话⭐️ 本篇文章将主要介绍离散化算法&#xff0c;所谓离散化算法&#xff0c;就是将一个无限区间上散点的数&#xff0c;在不改变相对大小的情况下&#xff0c;映射到一个较小的区间当中&#xff0c;然后对这个较小的区间进行操作的过程就是离散化的过程&#xff0…

【C++笔试强训】第二十八天

&#x1f387;C笔试强训 博客主页&#xff1a;一起去看日落吗分享博主的C刷题日常&#xff0c;大家一起学习博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a;夜色难免微凉&#xff0c;前方必有曙光 &#x1f31e;。 &#x1f4a6;&a…

微信小程序自定义tabBar(实操)

文章目录一、前言二、固定效果图实现步骤实现步骤完整代码-矢量图images图片app.json代码三、自定义效果图实现步骤实现步骤完整代码-矢量图images图片app.json代码custom-tab-bar下的代码使用自定义TaBar一、前言 一般使用tabBar的样式&#xff0c;固定不能改变。如下固定效果…

java计算机毕业设计springboot+vue村委会管理系统

项目介绍 本村委会管理系统是针对目前村委会管理的实际需求,从实际工作出发,对过去的村委会管理系统存在的问题进行分析,完善用户的使用体会。采用计算机系统来管理信息,取代人工管理模式,查询便利,信息准确率高,节省了开支,提高了工作的效率。 本系统结合计算机系统的结构、概…

DPDK-A3: KVM使用SRIOV和虚机使用DPDK

虚拟机基本管理 如下命令可以修改默认网段 sudo virsh net-edit --network default<network><name>default</name><uuid>45ed012c-3933-4f3e-9575-b37bffa21b83</uuid><forward modenat/><bridge namevirbr0 stpon delay0/><ma…

解决javax.xml.parsers.DocumentBuilderFactory.setFeature(Ljava/lang/String;Z)V异常

文章目录异常&#xff1a;不同jar包的多xml解析器冲突解决其他异常&#xff1a; java.lang.AbstractMethodError:javax.xml.parsers.DocumentBuilderFactory.setFeature(Ljava/lang/String;Z)可能原因&#xff1a; 在本地WINDOWS编译测试没问题&#xff0c;只在LINUX服务器上面…

通讯录的实现【涉及动态内存管理和文件操作】【从易到难】【详解】

本期介绍&#x1f356; 主要介绍&#xff1a;如何实现一个通讯录&#xff0c;从静态版通讯录&#xff0c;到动态内存版通讯录&#xff0c;再到文件存储版通讯录&#xff0c;详细讲述了每一个通讯录的实现步骤以及思维逻辑&#xff0c;以及通讯录的完整代码&#x1f440;。 文章…

基于Springboot+mybatis+mysql+html图书管理系统

基于Springbootmybatismysqlhtml图书管理系统一、系统介绍二、功能展示1.用户登陆2.图书管理3.读者管理4.借还管理5.密码修改6.图书查询&#xff08;读者&#xff09;7.个人信息&#xff08;读者&#xff09;8.我的借还&#xff08;读者&#xff09;一、系统介绍 系统主要功能…

深究为啥Vue管理的函数不能是箭头函数

首先明确一点&#xff0c;箭头函数的this指向是根据上下文作用域确定的 Vue框架中&#xff0c;容易搞错的一点就是认为对象也有作用域 了解作用域与作用域链这个问题就迎刃而解了 假设Vue管理的函数是箭头函数时&#xff1a; 此时this是windows&#xff0c;Vue中data与metho…

生物识别技术在汽车领域带来了巨大变革

智能汽车时代 2022年10月28日&#xff0c;工信部发布《道路机动车辆生产准入许可管理条例&#xff08;征求意见稿&#xff09;》&#xff08;“《准入管理条例草案》”&#xff09;。包含了更全面的汽车准入管理规定&#xff0c;同时较为系统地增加了针对智能汽车的准入管理规定…

更简单的读取和存储对象

在上一篇文章中我们已经介绍在XML文件注册Bean的具体步骤,这一篇文章将会介绍使用更加简洁的方式(使用注解)来存储和读取Bean.这也是最常用的方法. 1. 创建并配置好Spring项目 和上一篇的步骤相同,下面就相当于复习如何创建Spring项目吧 创建一个 Maven 项目为 Spring 项目…

微信小程序 | 酷炫时钟样式整理【附源码】

&#x1f4cc;个人主页&#xff1a;个人主页 ​&#x1f9c0; 推荐专栏&#xff1a;小程序开发成神之路 --【这是一个为想要入门和进阶小程序开发专门开启的精品专栏&#xff01;从个人到商业的全套开发教程&#xff0c;实打实的干货分享&#xff0c;确定不来看看&#xff1f; …

对给定的数组进行重新排列numpy.random.shuffle()和numpy.random.permutation()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 对给定的数组进行重新排列 numpy.random.shuffle()和 numpy.random.permutation() [太阳]选择题 请问对以下Python代码说法错误的是&#xff1f; import numpy as np anp.arange(6) print(【…

零基础带你基于vue2架构搭建qiankun父子项目微前端架构

这里建议大家用 14版本左右的node版本 我们先创建一个目录 就叫qiankun 然后在终端打开 qiankun 目录 在终端输入指令 vue create vue-qiankun-base创建一个叫 vue-qiankun-base的vue项目 版本大家先选择vue2 vue-qiankun-base项目将作为我们的基座 然后在终端输入 vue …

SpringBoot SpringBoot 开发实用篇 5 整合第三方技术 5.3 手机验证码案例 - 生成验证码

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇5 整合第三方技术5.3 手机验证码案例 - 生成验证码5.3.1 SpringBoot …

众焱公司网络平台建设-传输网的规划与设计

目 录 摘 要 I Abstract II 第一章 项目概述 1 1.1 项目目标 1 1.1.1 总体目标 1 1.1.2 阶段目标 1 1.2 设计原则 2 1.3总体拓扑图设计 3 第二章 应用分析 4 2.1 应用分类 4 2.1.1 应用系统总体框架 4 2.1.2 业务系统应用分类 5 2.1.3 信息管理系统应用分类 6 2.2 数据中心及分…

数据结构:栈和队列

栈是一种特殊的线性结构&#xff0c;只允许在栈顶进行进行插入和删除操作。 进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出&#xff08;先进后出&#xff09;LIFO&#xff08;Last In First Out&#xff09;的原则。 类比成将子…