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
    排序列;

执行顺序(逻辑层面):

  1. FROM + WHERE:先定位表并过滤出符合条件的行。
  2. GROUP BY:将过滤后的数据按指定列分组。
  3. 聚合计算:在每个分组内执行聚合函数。
  4. SELECT:输出分组列和聚合结果。
  5. 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 的协作关系:

GROUP BY 聚合查询执行流程 GROUP BY 聚合查询执行流程 原始数据表 loan_application WHERE 过滤 筛选符合条件的行 GROUP BY 分组 按 risk_level 分组 聚合计算 COUNT / SUM / AVG HAVING 过滤 过滤聚合结果 最终结果集 执行顺序说明 1. FROM → 定位表 2. WHERE → 行级过滤(聚合前) 3. GROUP BY → 分组 4. 聚合函数 → 每组计算 5. 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)。这种写法在风控规则引擎中常用于条件计数和条件求和,避免多次扫描表。