2. SQL基础回顾:SELECT、WHERE、GROUP BY、HAVING、ORDER BY、JOIN等核心语法在催收数据中的应用
在催收特征筛选工作中,SQL是提取、清洗、聚合和关联数据的核心工具。无论是计算逾期天数、统计客户历史还款行为,还是拼接多张业务表以构建完整的特征宽表,都离不开SQL基础语法的灵活运用。本节将结合催收业务场景,系统回顾最常用的SQL核心语法。
2.1 数据查询与过滤:SELECT + WHERE
SELECT 用于指定要查询的字段,WHERE 用于按条件过滤记录。在催收数据中,最常见的过滤条件是逾期状态、案件阶段、客户分组等。
业务场景示例: 从逾期案件表
overdue_case 中提取所有逾期天数超过30天且当前余额大于5000元的案件ID和客户姓名。
SELECT
case_id,
customer_name,
overdue_days,
current_balance
FROM overdue_case
WHERE overdue_days > 30
AND current_balance > 5000;
| 关键字 | 催收业务常用场景 | 注意事项 |
|---|---|---|
| SELECT | 选择案件ID、客户ID、逾期天数、分配催收员等字段 | 避免使用 SELECT *,应明确列出所需字段以提升性能 |
| WHERE | 过滤逾期天数 > N、案件状态 = '已分配'、催收员ID = 指定值 | 注意 NULL 值的处理,使用 IS NULL 或 IS NOT NULL |
| AND / OR | 组合多个条件,如逾期天数 > 30 且 余额 > 10000 | 使用括号明确优先级,避免逻辑歧义 |
| IN / NOT IN | 筛选指定催收员列表、特定产品类型 | IN 列表不宜过长,否则考虑使用临时表或 JOIN |
| LIKE | 模糊匹配客户姓名、地址等文本字段 | 避免前置通配符 %,会导致索引失效 |
技巧提示: 在特征筛选阶段,WHERE 条件应尽量使用索引字段(如案件ID、分配日期),以加速数据提取。对于大表,避免在 WHERE 中对字段进行函数运算(如
WHERE DATE(create_time) = '2025-01-01'),应改写为范围条件(如 WHERE create_time >= '2025-01-01' AND create_time < '2025-01-02')。
2.2 数据聚合与分组:GROUP BY + HAVING
GROUP BY 用于按一个或多个字段对数据进行分组,配合聚合函数(如 COUNT、SUM、AVG、MAX、MIN)计算每组的统计值。HAVING 用于对分组后的结果进行过滤,类似于 WHERE 但作用于聚合后的数据。
业务场景示例: 统计每个催收员名下逾期天数超过60天的案件数量,并筛选出案件数超过10个的催收员。
SELECT
collector_id,
COUNT(case_id) AS high_risk_case_count,
AVG(current_balance) AS avg_balance
FROM overdue_case
WHERE overdue_days > 60
GROUP BY collector_id
HAVING COUNT(case_id) > 10
ORDER BY high_risk_case_count DESC;
| 语法元素 | 催收业务应用 | 执行顺序 |
|---|---|---|
| GROUP BY | 按催收员、产品类型、逾期区间、城市等分组 | 在 WHERE 之后,HAVING 之前 |
| 聚合函数 | COUNT(案件数)、SUM(逾期金额)、AVG(逾期天数)、MAX(最长逾期) | 在 GROUP BY 分组后对每组计算 |
| HAVING | 过滤分组后的统计结果,如案件数 > 10、平均余额 > 5000 | 在 GROUP BY 之后,ORDER BY 之前 |
| WHERE vs HAVING | WHERE 过滤原始行,HAVING 过滤分组后的聚合结果 | WHERE 先执行,减少分组数据量 |
常见错误: 在 SELECT 中使用了非聚合字段但未将其包含在 GROUP BY 中。例如:
SELECT collector_id, case_id, COUNT(*) FROM overdue_case GROUP BY collector_id 会报错,因为 case_id 不在 GROUP BY 中且未被聚合。解决方案:要么将 case_id 加入 GROUP BY,要么使用聚合函数(如 MAX(case_id))包裹。
2.3 数据排序:ORDER BY
ORDER BY 用于对查询结果按一个或多个字段进行升序(ASC)或降序(DESC)排序。在催收特征筛选中,排序常用于查看风险最高的案件、最紧急的待办事项等。
-- 按逾期天数降序排列,查看风险最高的前20个案件
SELECT
case_id,
customer_name,
overdue_days,
current_balance
FROM overdue_case
WHERE case_status = '待催收'
ORDER BY overdue_days DESC, current_balance DESC
LIMIT 20;
排序优化: ORDER BY 字段应尽量使用索引,避免对大结果集进行文件排序(filesort)。如果排序字段较多,可以考虑建立复合索引。在特征筛选的探索阶段,可以先 LIMIT 少量数据测试排序结果,确认无误后再全量执行。
2.4 多表关联:JOIN
催收数据通常分散在多张表中,如客户信息表、案件表、还款记录表、催收通话记录表等。JOIN 用于将这些表按关联键连接起来,构建包含完整信息的特征宽表。
| JOIN 类型 | 催收业务场景 | 返回结果 |
|---|---|---|
| INNER JOIN | 只保留两表中匹配成功的记录,如案件表与客户表通过客户ID关联 | 仅匹配成功的行 |
| LEFT JOIN | 保留左表全部记录,右表无匹配时填充 NULL,如案件表左连还款记录表 | 左表全部 + 右表匹配行 |
| RIGHT JOIN | 保留右表全部记录,左表无匹配时填充 NULL(较少使用) | 右表全部 + 左表匹配行 |
| FULL OUTER JOIN | 保留两表全部记录,无匹配时填充 NULL(部分数据库不支持) | 两表全部行 |
业务场景示例: 将逾期案件表与客户信息表关联,同时左连最近一次还款记录,构建包含客户基本信息和还款行为的特征宽表。
SELECT
oc.case_id,
oc.overdue_days,
oc.current_balance,
ci.customer_name,
ci.credit_score,
ci.income_level,
rp.last_repayment_date,
rp.last_repayment_amount
FROM overdue_case oc
INNER JOIN customer_info ci ON oc.customer_id = ci.customer_id
LEFT JOIN (
SELECT
customer_id,
MAX(repayment_date) AS last_repayment_date,
MAX(repayment_amount) AS last_repayment_amount
FROM repayment_record
GROUP BY customer_id
) rp ON oc.customer_id = rp.customer_id
WHERE oc.overdue_days > 30;
2.5 子查询与公用表表达式(CTE)
在催收特征筛选过程中,经常需要先计算一些中间统计量,再基于这些统计量进行进一步筛选。子查询和 CTE 是组织复杂查询的两种有效方式。
业务场景示例: 找出每个催收员名下逾期金额超过该催收员平均逾期金额的案件。
-- 使用 CTE 方式(推荐,可读性更好)
WITH collector_avg AS (
SELECT
collector_id,
AVG(current_balance) AS avg_balance
FROM overdue_case
WHERE overdue_days > 0
GROUP BY collector_id
)
SELECT
oc.case_id,
oc.collector_id,
oc.current_balance,
ca.avg_balance
FROM overdue_case oc
INNER JOIN collector_avg ca ON oc.collector_id = ca.collector_id
WHERE oc.current_balance > ca.avg_balance
ORDER BY oc.collector_id, oc.current_balance DESC;
| 方式 | 优点 | 适用场景 |
|---|---|---|
| 子查询(FROM 子句中) | 无需额外命名,适合简单的一次性计算 | 子查询只使用一次,逻辑简单 |
| CTE(WITH 子句) | 可读性强,可被多次引用,支持递归 | 复杂查询、需要多次引用中间结果、递归查询(如组织层级) |
| 标量子查询(SELECT 子句中) | 直接在 SELECT 中计算单值 | 需要为每行计算一个关联的标量值 |
性能建议: 在催收大数据场景下,CTE 通常比多层嵌套子查询性能更好,因为数据库可以优化 CTE 的执行计划。如果 CTE 被多次引用,数据库可能只执行一次并缓存结果。但要注意,并非所有数据库引擎都支持 CTE 的物化(Materialization),建议在实际环境中测试执行计划。
2.6 综合实战:构建催收特征基础宽表
将上述语法综合运用,构建一个用于催收模型训练的客户特征宽表。该宽表将包含客户基本信息、逾期行为特征、历史还款特征和催收交互特征。
WITH
-- 1. 客户基础信息
customer_base AS (
SELECT
customer_id,
customer_name,
credit_score,
income_level,
city,
registration_date
FROM customer_info
WHERE is_active = 1
),
-- 2. 逾期行为特征
overdue_features AS (
SELECT
customer_id,
COUNT(case_id) AS total_overdue_cases,
MAX(overdue_days) AS max_overdue_days,
AVG(overdue_days) AS avg_overdue_days,
SUM(current_balance) AS total_overdue_balance,
AVG(current_balance) AS avg_overdue_balance
FROM overdue_case
WHERE case_status IN ('待催收', '催收中')
GROUP BY customer_id
),
-- 3. 历史还款特征
repayment_features AS (
SELECT
customer_id,
COUNT(repayment_id) AS total_repayment_count,
SUM(repayment_amount) AS total_repayment_amount,
AVG(repayment_amount) AS avg_repayment_amount,
MAX(repayment_date) AS last_repayment_date,
DATEDIFF(CURRENT_DATE, MAX(repayment_date)) AS days_since_last_repayment
FROM repayment_record
GROUP BY customer_id
)
-- 4. 关联生成特征宽表
SELECT
cb.customer_id,
cb.customer_name,
cb.credit_score,
cb.income_level,
cb.city,
COALESCE(of.total_overdue_cases, 0) AS total_overdue_cases,
COALESCE(of.max_overdue_days, 0) AS max_overdue_days,
COALESCE(of.avg_overdue_days, 0) AS avg_overdue_days,
COALESCE(of.total_overdue_balance, 0) AS total_overdue_balance,
COALESCE(rf.total_repayment_count, 0) AS total_repayment_count,
COALESCE(rf.total_repayment_amount, 0) AS total_repayment_amount,
rf.days_since_last_repayment
FROM customer_base cb
LEFT JOIN overdue_features of ON cb.customer_id = of.customer_id
LEFT JOIN repayment_features rf ON cb.customer_id = rf.customer_id
WHERE of.total_overdue_cases IS NOT NULL -- 只保留有逾期记录的客户
ORDER BY total_overdue_balance DESC;
注意事项: 在构建特征宽表时,务必注意 NULL 值的处理。使用
COALESCE 函数将 NULL 替换为 0 或其他默认值,避免模型训练时出现缺失值错误。同时,LEFT JOIN 后要确认关联键是否唯一,避免因一对多关系导致数据膨胀(例如一个客户有多个案件,关联后行数会翻倍)。
2.7 本章小结
本节系统回顾了SQL核心语法在催收数据中的具体应用:
- SELECT + WHERE:基础数据提取与条件过滤,注意索引优化和NULL处理
- GROUP BY + HAVING:分组聚合统计,用于计算催收员绩效、逾期区间分布等
- ORDER BY:结果排序,常用于风险排序和优先级管理
- JOIN:多表关联,构建特征宽表的核心手段,注意关联键的唯一性
- 子查询与CTE:组织复杂查询,提升可读性和维护性