3、聚合函数与分组统计:COUNT、SUM、AVG、MIN、MAX函数详解,GROUP BY与HAVING子句在质量探查中的应用
数据质量探查的核心任务之一,是对数据集进行宏观与微观的量化评估。聚合函数与分组统计正是完成这一任务的基础工具。通过 COUNT、SUM、AVG、MIN、MAX 这五大聚合函数,可以快速掌握字段的完整性、数值范围与分布特征;结合 GROUP BY 与 HAVING 子句,则能深入发现不同维度下的数据异常与质量缺陷。
3.1 五大聚合函数在质量探查中的角色
聚合函数对一组值执行计算并返回单个汇总值。在数据质量场景中,每个函数都对应特定的探查目标:
| 函数 | 质量探查目标 | 典型应用场景 |
|---|---|---|
COUNT(*) |
记录总数、行级完整性 | 探查表是否为空、对比源端与目标端行数 |
COUNT(column) |
非空值数量、字段填充率 | 计算某字段的缺失比例 |
COUNT(DISTINCT column) |
唯一值数量、基数 | 探查主键是否重复、枚举值是否合理 |
SUM(column) |
数值总量、汇总一致性 | 验证金额字段合计是否与业务台账一致 |
AVG(column) |
数值中心趋势、异常值敏感度 | 探查平均单价是否在合理区间 |
MIN(column) |
最小值、边界异常 | 发现日期字段是否出现 1900-01-01 或负数 |
MAX(column) |
最大值、边界异常 | 发现金额字段是否出现极端大值 |
探查技巧:在质量探查初期,建议对每个关键字段同时执行
COUNT(column) 与 COUNT(*),两者之差即为缺失值数量。这是计算字段完整率最直接的方法。
3.2 聚合函数实战:字段级质量探查
以下示例基于一张电商订单表 orders,包含字段:order_id、customer_id、order_amount、order_date、region。
3.2.1 探查字段完整性与基数
-- 探查订单表的基本质量指标
SELECT
COUNT(*) AS 总行数,
COUNT(order_id) AS 订单ID非空数,
COUNT(DISTINCT order_id) AS 唯一订单ID数,
COUNT(customer_id) AS 客户ID非空数,
COUNT(DISTINCT customer_id) AS 唯一客户数,
COUNT(region) AS 区域非空数,
COUNT(DISTINCT region) AS 区域枚举数
FROM orders;
质量解读:若 COUNT(order_id) 与 COUNT(DISTINCT order_id) 不相等,说明存在重复的订单ID,这是严重的数据一致性问题。若 COUNT(region) 远小于 COUNT(*),则区域字段缺失严重。
3.2.2 探查数值字段的边界与分布
-- 探查金额字段的质量边界
SELECT
COUNT(order_amount) AS 金额非空数,
MIN(order_amount) AS 最小金额,
MAX(order_amount) AS 最大金额,
AVG(order_amount) AS 平均金额,
SUM(order_amount) AS 总金额
FROM orders;
边界异常警示:当
MIN(order_amount) 出现负数或 0 时,需排查是否存在退款单未标记或测试数据混入。当 MAX(order_amount) 远高于业务上限(如单笔超过 100 万),应标记为疑似异常值,纳入后续探查范围。
3.3 GROUP BY:按维度分组探查质量
全局聚合只能看到整体情况,而数据质量问题往往隐藏在特定维度中。GROUP BY 允许按一个或多个字段分组,逐组计算聚合指标,从而发现局部异常。
3.3.1 按区域探查订单质量
-- 按区域分组探查:发现哪些区域数据质量最差
SELECT
region,
COUNT(*) AS 订单数,
COUNT(customer_id) AS 客户ID非空数,
COUNT(order_amount) AS 金额非空数,
ROUND(100.0 * (COUNT(*) - COUNT(customer_id)) / COUNT(*), 2) AS 客户缺失率_pct,
MIN(order_amount) AS 最小金额,
MAX(order_amount) AS 最大金额,
AVG(order_amount) AS 平均金额
FROM orders
GROUP BY region
ORDER BY 客户缺失率_pct DESC;
质量发现:通过 ORDER BY 客户缺失率_pct DESC,可以快速定位哪些区域的数据录入质量最差,为后续数据清洗提供优先级依据。
3.4 HAVING:筛选分组后的质量缺陷
WHERE 子句在分组前过滤行,而 HAVING 子句在分组后过滤组。在质量探查中,HAVING 用于筛选出那些满足特定质量阈值(如缺失率过高、重复行数过多)的分组。
3.4.1 筛选缺失率超过阈值的分组
-- 只保留客户ID缺失率超过 5% 的区域
SELECT
region,
COUNT(*) AS 订单数,
COUNT(customer_id) AS 客户ID非空数,
ROUND(100.0 * (COUNT(*) - COUNT(customer_id)) / COUNT(*), 2) AS 客户缺失率_pct
FROM orders
GROUP BY region
HAVING (COUNT(*) - COUNT(customer_id)) * 1.0 / COUNT(*) > 0.05
ORDER BY 客户缺失率_pct DESC;
3.4.2 筛选存在重复主键的分组
-- 按客户分组,找出存在重复订单的客户
SELECT
customer_id,
COUNT(*) AS 订单数,
COUNT(DISTINCT order_id) AS 唯一订单数,
COUNT(*) - COUNT(DISTINCT order_id) AS 重复订单数
FROM orders
GROUP BY customer_id
HAVING COUNT(*) > COUNT(DISTINCT order_id)
ORDER BY 重复订单数 DESC;
HAVING vs WHERE 速记:
WHERE 过滤原始行(分组前),HAVING 过滤分组结果(分组后)。HAVING 中可以使用聚合函数(如 COUNT(*)、SUM(amount)),而 WHERE 中不允许直接使用聚合函数。
3.5 综合实战:多维度质量探查报告
将上述技术整合,生成一份按日期和区域交叉分组的质量探查报告:
-- 按日期和区域交叉分组,输出完整质量指标
SELECT
order_date,
region,
COUNT(*) AS 总订单数,
COUNT(customer_id) AS 有效客户数,
COUNT(order_amount) AS 有效金额数,
ROUND(AVG(order_amount), 2) AS 平均金额,
MIN(order_amount) AS 最小金额,
MAX(order_amount) AS 最大金额,
SUM(order_amount) AS 总金额,
COUNT(DISTINCT customer_id) AS 独立客户数
FROM orders
WHERE order_date >= '2024-01-01' -- 先过滤近期数据
GROUP BY order_date, region
HAVING COUNT(*) > 10 -- 只保留订单数超过10的分组
AND (COUNT(*) - COUNT(customer_id)) * 1.0 / COUNT(*) < 0.1 -- 客户缺失率低于10%
ORDER BY order_date, region;
质量探查最佳实践:建议将上述查询结果存入一张质量探查日志表,每日或每周定时运行,形成质量趋势基线。当某个分组的
MIN(order_amount) 突然变为负数,或 COUNT(DISTINCT customer_id) 骤降时,系统应自动触发告警。
3.6 常见陷阱与避坑指南
- 陷阱1:
COUNT(*)与COUNT(1)等价,都统计所有行;COUNT(column)只统计该列非空的行。混淆两者会导致缺失率计算错误。 - 陷阱2:
AVG(column)会自动忽略 NULL 值,若想将 NULL 视为 0 参与平均,需先使用COALESCE(column, 0)。 - 陷阱3:
GROUP BY的字段必须出现在SELECT列表中(除非该字段在聚合函数中),否则 SQL 语法报错。 - 陷阱4:
HAVING中不能使用列的别名(如HAVING 客户缺失率_pct > 5),必须重复写完整的聚合表达式。部分数据库(如 MySQL)允许别名,但为兼容性建议写完整表达式。 - 陷阱5:当
GROUP BY的字段基数很高(如订单ID),分组后的组数可能极大,导致查询性能下降。此时应先用WHERE缩小数据范围。
性能警示:在大表上执行
COUNT(DISTINCT column) 是非常消耗资源的操作。如果只是探查唯一值的大致数量,可以使用 APPROX_COUNT_DISTINCT(适用于 BigQuery、Redshift、Spark SQL)或 HyperLogLog 算法,在精度与性能之间取得平衡。