DuckDB教程:数据汇总 DuckDB 中的聚合函数Aggregate Functions用于将多行数据汇总为单个结果值是数据分析中最常用的 SQL 功能之一。以下是一些常见的聚合函数count()返回查询结果或者表中的行数。avg()计算一组数据的平均值。sum()计算一组数值的总和。max()返回一组数据中的最大值。min()返回一组数据中的最小值。string_agg()将一组字符串合并成一个字符串。DuckDB 中完整的聚合函数语法如下其中聚合函数中的参数支持 DISTINCT 关键字表示在计算之前排除重复数据。聚合函数支持可选的 FILTER 子句只针对满足特定条件的数据进行汇总。聚合函数在计算时忽略数据中的 NULL 值COUNT(*)、list()、first()、last() 函数除外。使用 count() 函数统计行数count(*)/count() 函数用于统计查询结果或者表中的行数。例如以下语句统计了员工的数量SELECTcount(*)AS员工数量FROMemployee;┌──────────┐ │ 员工数量 │ │ int64 │ ├──────────┤ │25│ └──────────┘员工表中包含 25 条记录也就是 25 名员工。count()函数也可以统计某个字段或者表达式不为空值的数量例如SELECTcount(emp_id),count(0)FROMemployee;┌───────────────┬──────────┐ │count(emp_id)│count(0)│ │ int64 │ int64 │ ├───────────────┼──────────┤ │25│25│ └───────────────┴──────────┘两个 count() 函数分别统计了员工编号和常量 0 不为空的数量两个结果都是 25因为每个员工都有一个编号而 count(0) 和 count(*) 的结果相同。以下查询在 count() 函数中使用了 DISTINCT 关键字SELECTcount(sex)AS所有性别,count(DISTINCTsex)AS不同性别FROMemployee;┌──────────┬──────────┐ │ 所有性别 │ 不同性别 │ │ int64 │ int64 │ ├──────────┼──────────┤ │25│2│ └──────────┴──────────┘员工表中的不同性别只有“男”和“女”因此使用 DISTINCT 关键字之后的结果为 2。提示除了 DISTINCT 关键字之外我们也可以使用 ALL 关键字表示汇总时不排除重复数据。因为 ALL 是默认值所以我们通常省略。另外如果参数中存在空值count() 函数会忽略这些空值。以下查询统计了员工拥有奖金的情况SELECTcount(*)AS员工数量,count(bonus)AS拥有奖金,count(*)-count(bonus)AS没有奖金FROMemployee;┌──────────┬──────────┬──────────┐ │ 员工数量 │ 拥有奖金 │ 没有奖金 │ │ int64 │ int64 │ int64 │ ├──────────┼──────────┼──────────┤ │25│9│16│ └──────────┴──────────┴──────────┘查询结果显示 9 名员工拥有奖金16 名员工没有奖金。针对以上查询中“没有奖金”的员工统计还可以使用 FILTER 子句实现SELECTcount(*)AS员工数量,count(bonus)AS拥有奖金,count(*)FILTER(bonusISNULL)AS没有奖金FROMemployee;┌──────────┬──────────┬──────────┐ │ 员工数量 │ 拥有奖金 │ 没有奖金 │ │ int64 │ int64 │ int64 │ ├──────────┼──────────┼──────────┤ │25│9│16│ └──────────┴──────────┴──────────┘FILTER 子句的作用类似于 WHERE 子句但是它只对当前聚合函数生效。使用 avg() 函数计算平均值avg() 函数用于计算一组数据的平均值。例如以下查询统计了所有员工的平均月薪SELECTavg(salary)AS平均月薪FROMemployee;┌──────────┐ │ 平均月薪 │ │double│ ├──────────┤ │9832.0│ └──────────┘所有员工的平均月薪为 9832。如果我们为 avg() 函数指定了 DISTINCT 关键字则会在计算平均值之前排除重复数据。例如1、1、2 的平均值为 (12)/2而不是 (112)/3。以下查询返回了所有不重复月薪的平均值SELECTavg(DISTINCTsalary)AS平均月薪FROMemployee;┌──────────┐ │ 平均月薪 │ │double│ ├──────────┤ │9865.0│ └──────────┘去掉重复数据之后的平均月薪为有所增加说明低薪水的员工更多。另外如果参数中存在空值avg() 函数会忽略这些空值。例如1、2、NULL 的平均值为 (12)/2而不是 (12NULL)/3。以下查询返回了员工不包括没有奖金的员工的平均奖金SELECTavg(bonus)AS平均奖金FROMemployee;┌───────────────────┐ │ 平均奖金 │ │double│ ├───────────────────┤ │6388.888888888889│ └───────────────────┘如果我们想要将没有奖金的员工当作奖金为零处理可以使用 CASE 表达式SELECTAVG(CASEWHENbonusISNULLTHEN0ELSEbonusEND)AS平均奖金FROMemployee;以下查询使用 FILTER 子句同时统计了不同性别员工的平均月薪SELECTavg(salary)FILTER(sex男)AS男性平均月薪,avg(salary)FILTER(sex女)AS女性平均月薪FROMemployee;┌────────────────────┬──────────────┐ │ 男性平均月薪 │ 女性平均月薪 │ │double│double│ ├────────────────────┼──────────────┤ │10054.545454545454│8200.0│ └────────────────────┴──────────────┘使用 sum() 函数计算总和sum() 函数用于计算一组数值的总和。例如以下语句返回了所有员工的月薪总和SELECTsum(salary)AS月薪总和FROMemployee;┌───────────────┐ │ 月薪总和 │ │decimal(38,2)│ ├───────────────┤ │245800.00│ └───────────────┘公司所有员工每个月的薪水总和为245800。sum() 函数也可以利用 DISTINCT 关键字在计算总和之前排除重复数据但是一般很少使用。另外如果参数中存在空值sum()函数会忽略这些空值。以下查询返回了所有员工的平均奖金没有奖金的员工当作奖金为零处理SELECTsum(bonus)/count(*)AS平均奖金FROMemployee;查询返回的结果和上一小节中的 CASE 表达式示例相同。使用 max()/min() 函数返回最大值/最小值max() 函数用于返回一组数据中的最大值min() 函数用于返回一组数据中的最小值。例如以下查询返回了最晚入职和最早入职的员工入职时间SELECTmax(hire_date)AS最晚入职,min(hire_date)AS最早入职FROMemployee;┌────────────┬────────────┐ │ 最晚入职 │ 最早入职 │ │date│date│ ├────────────┼────────────┤ │2019-05-11│2000-01-01│ └────────────┴────────────┘最后一位员工的入职时间是 2019 年 5 月 11 日第一位员工的入职时间是 2000 年 1 月 1 日。max()/min() 函数支持 DISTINCT 关键字但是没有实际意义因为它对结果没有影响。另外如果参数中存在空值max()/min() 函数会忽略这些空值。使用 string_agg() 函数合并字符串string_agg() 函数用于对字符串进行聚合可以将多行字符串合并成单个字符串。例如以下查询返回了行政管理部门中所有员工的电子邮箱SELECTstring_agg(email,;)AS收件人FROMemployeeWHEREdept_id1;┌─────────────────────────────────────────────────────────┐ │ 收件人 │ │varchar│ ├─────────────────────────────────────────────────────────┤ │ liubeishuguo.com;guanyushuguo.com;zhangfeishuguo.com│ └─────────────────────────────────────────────────────────┘其中第 2 个参数指定了连接字符串的分隔符默认使用逗号进行连接。group_concat(arg, sep)、listagg(arg, sep) 函数都是 string_agg(arg, sep) 的同义词。另外我们还可以在参数中使用 ORDER BY 对字符串进行排序。例如SELECTstring_agg(emailORDERBYemp_idDESC)AS收件人FROMemployeeWHEREdept_id1;┌─────────────────────────────────────────────────────────┐ │ 收件人 │ │varchar│ ├─────────────────────────────────────────────────────────┤ │ zhangfeishuguo.com,guanyushuguo.com,liubeishuguo.com│ └─────────────────────────────────────────────────────────┘string_agg() 函数可以使用 DISTINCT 关键字在合并之前排除重复数据同时还会忽略数据中的 NULL 值。