3. 基础SQL回顾:GROUP BY与聚合函数(COUNT、SUM、AVG、MAX、MIN)的用法与注意事项
在授信决策引擎中,我们经常需要按客户群体、产品类型或时间批次对数据进行汇总统计。例如:“统计每个风险等级下的客户数量”、“计算各产品线的平均授信额度”或“找出单日最大放款金额”。这些操作的核心就是 GROUP BY 子句与 聚合函数 的组合使用。
3.1 聚合函数速览
聚合函数用于对一组值执行计算并返回单个汇总值。在风控SQL中,最常用的五个聚合函数如下:
| 函数 | 功能 | 适用数据类型 | 常见风控场景 |
|---|---|---|---|
COUNT(*) / COUNT(column) |
统计行数;COUNT(*) 包含NULL行,COUNT(column) 忽略NULL |
任意 | 统计申请单总量、命中规则数 |
SUM(column) |
返回数值列的总和 | 数值 | 计算批次总授信金额、总放款额 |
AVG(column) |
返回数值列的平均值 | 数值 | 计算平均额度、平均利率 |
MAX(column) |
返回数值列的最大值 | 数值、日期、字符串 | 找出单笔最大风险敞口、最新申请时间 |
MIN(column) |
返回数值列的最小值 | 数值、日期、字符串 | 找出最低信用评分、最早逾期日期 |
关键区别:
COUNT(*) 统计所有行,包括某列全为NULL的行;而 COUNT(column_name) 只统计该列非NULL的行数。在风控数据清洗时,若某字段缺失较多,两者结果可能差异显著。
3.2 GROUP BY 基本语法与执行逻辑
GROUP BY 子句将查询结果按一个或多个列进行分组,然后对每个分组应用聚合函数。其基本语法结构如下:
SELECT
分组列1,
分组列2,
聚合函数(列名) AS 别名
FROM
表名
WHERE
过滤条件
GROUP BY
分组列1,
分组列2
ORDER BY
排序列;
执行顺序(逻辑层面):
- FROM + WHERE:先定位表并过滤出符合条件的行。
- GROUP BY:将过滤后的数据按指定列分组。
- 聚合计算:在每个分组内执行聚合函数。
- SELECT:输出分组列和聚合结果。
- ORDER BY:对最终结果排序。
重要规则: SELECT 子句中出现的非聚合列,必须全部出现在 GROUP BY 子句中。否则数据库会报错(在严格SQL模式下)或返回不确定结果。
3.3 典型风控场景示例
假设我们有一张 loan_application 表,包含字段:batch_id(批次号)、risk_level(风险等级)、approved_amount(审批金额)、decision_time(决策时间)。
场景一:按风险等级统计申请量和总金额
SELECT
risk_level,
COUNT(*) AS application_count,
SUM(approved_amount) AS total_approved_amount
FROM loan_application
WHERE decision_time >= '2025-01-01'
GROUP BY risk_level
ORDER BY total_approved_amount DESC;
场景二:按批次计算平均额度与最大单笔额度
SELECT
batch_id,
AVG(approved_amount) AS avg_amount,
MAX(approved_amount) AS max_amount,
MIN(approved_amount) AS min_amount
FROM loan_application
GROUP BY batch_id
ORDER BY batch_id;
3.4 常见陷阱与注意事项
| 陷阱 | 说明 | 正确做法 |
|---|---|---|
| SELECT 中混入非聚合、非分组列 | 如 SELECT risk_level, decision_time, COUNT(*) 但 GROUP BY risk_level,decision_time 未分组 |
要么将 decision_time 加入 GROUP BY,要么用聚合函数包裹(如 MAX(decision_time)) |
| WHERE 过滤聚合前的数据 | 误以为 WHERE 可以过滤聚合后的结果(如 WHERE COUNT(*) > 10) |
使用 HAVING 子句过滤聚合后的结果 |
| COUNT(*) 与 COUNT(1) 性能差异 | 部分开发者认为 COUNT(1) 更快 |
现代数据库中两者性能几乎一致,COUNT(*) 语义更清晰 |
| NULL 值对聚合的影响 | SUM(NULL) 返回 NULL;AVG 忽略 NULL 行 |
使用 COALESCE 或 IFNULL 处理可能为 NULL 的列 |
| GROUP BY 列的顺序 | 分组列的顺序不影响分组结果,但影响排序(部分数据库) | 明确使用 ORDER BY 控制输出顺序 |
性能警示: 在授信决策引擎的批量处理中,GROUP BY 操作通常发生在大量数据上。如果分组列没有合适的索引,会导致全表扫描和临时文件排序,严重影响吞吐量。对于高频批次查询,建议在
batch_id、risk_level 等分组列上建立复合索引。
3.5 聚合查询执行流程示意图
下图展示了从原始数据到分组聚合结果的完整执行流程,帮助理解 WHERE、GROUP BY、聚合函数、HAVING 的协作关系:
3.6 聚合函数与 NULL 值的交互
在风控数据中,缺失值(NULL)非常常见。理解聚合函数如何处理 NULL 至关重要:
- COUNT(column):忽略 NULL 值;COUNT(*) 包含所有行。
- SUM(column):如果所有值均为 NULL,则返回 NULL;否则忽略 NULL 值进行求和。
- AVG(column):忽略 NULL 值,分母只计非 NULL 行数。
- MAX / MIN:忽略 NULL 值,只比较非 NULL 值。
实战建议: 在计算平均授信额度时,如果某批次有大量申请因风控拒绝而金额为 NULL,直接使用
AVG(approved_amount) 会忽略这些拒绝记录,导致平均值偏高。此时应明确业务口径:是“所有申请的平均”还是“通过申请的平均”?前者需用 COALESCE(approved_amount, 0) 处理。
3.7 多列分组与聚合嵌套
授信决策引擎中常需要按多个维度进行下钻分析。多列分组只需在 GROUP BY 后列出所有维度列:
SELECT
batch_id,
risk_level,
COUNT(*) AS cnt,
SUM(approved_amount) AS total_amt,
AVG(approved_amount) AS avg_amt
FROM loan_application
GROUP BY batch_id, risk_level
ORDER BY batch_id, risk_level;
该查询按“批次 + 风险等级”的组合进行分组,输出每个组合的申请量、总金额和平均金额,是批次偏移定位分析的基础数据形态。
进阶提示: 聚合函数内部可以嵌套表达式,例如
AVG(COALESCE(approved_amount, 0)) 或 SUM(CASE WHEN status = 'APPROVED' THEN 1 ELSE 0 END)。这种写法在风控规则引擎中常用于条件计数和条件求和,避免多次扫描表。