3. SQL基础回顾:SELECT、WHERE、JOIN、GROUP BY、HAVING、ORDER BY等核心语法
在信用评分模型A/B测试中,SQL是数据提取、特征加工与效果评估的核心工具。无论是计算坏账率、对比实验组与对照组,还是生成PSM匹配样本,都离不开SQL的精准操作。本节将系统回顾SQL最常用的六大核心语法,并结合信用评分场景给出实战示例。
3.1 SELECT & FROM:数据检索基础
SELECT 用于指定要查询的字段,FROM 指定数据来源表。在信用评分项目中,通常从用户主表、交易流水表、逾期标签表中提取数据。
信用评分场景示例: 提取申请用户的基础信息与最终逾期标签。
-- 提取申请用户ID、年龄、收入等级与最终逾期标签
SELECT
user_id,
age,
income_level,
is_default_90dpd
FROM credit_application
WHERE application_date BETWEEN '2024-01-01' AND '2024-06-30';
| 关键字 | 作用 | 信用评分常见用法 |
|---|---|---|
| SELECT | 指定返回的列 | 选择特征字段、标签字段、分组统计值 |
| FROM | 指定数据表 | 关联用户表、交易表、催收表 |
| AS | 设置别名 | 简化长表名或字段名,提高可读性 |
3.2 WHERE:条件过滤
WHERE 用于在数据提取阶段过滤行记录。在A/B测试中,常用来筛选实验组、对照组或特定时间窗口的数据。
避坑提示: 在信用评分模型中,
WHERE 条件中避免使用函数包裹索引字段(如 WHERE DATE(apply_time) = '2024-01-01'),会导致索引失效,应使用范围查询 WHERE apply_time >= '2024-01-01' AND apply_time < '2024-01-02'。
-- 筛选A/B测试中的实验组用户(策略A)
SELECT
user_id,
credit_score,
loan_amount
FROM credit_application
WHERE experiment_group = 'treatment_A'
AND application_date >= '2024-03-01'
AND loan_amount BETWEEN 5000 AND 50000;
3.3 JOIN:多表关联
信用评分建模通常需要将用户基本信息、交易行为、历史逾期记录合并到一张宽表中。JOIN 是实现多表关联的核心操作。
-- 将用户主表与交易流水表、逾期标签表关联
SELECT
a.user_id,
a.age,
a.income_level,
b.total_trans_amount,
c.is_default_90dpd
FROM credit_user a
LEFT JOIN (
SELECT user_id, SUM(trans_amount) AS total_trans_amount
FROM transaction_record
WHERE trans_date >= '2024-01-01'
GROUP BY user_id
) b ON a.user_id = b.user_id
LEFT JOIN default_label c ON a.user_id = c.user_id
WHERE a.application_date >= '2024-01-01';
| JOIN类型 | 返回结果 | 信用评分使用场景 |
|---|---|---|
| INNER JOIN | 仅返回匹配成功的记录 | 只分析有完整交易记录的用户 |
| LEFT JOIN | 保留左表全部记录,右表无匹配则填NULL | 保留所有申请用户,交易数据缺失不影响主表 |
| RIGHT JOIN | 保留右表全部记录 | 较少使用,可用LEFT JOIN改写 |
| FULL OUTER JOIN | 保留两表全部记录 | 数据对账、全量用户画像合并 |
3.4 GROUP BY & HAVING:分组聚合与过滤
GROUP BY 按指定字段分组,配合聚合函数(COUNT、SUM、AVG、MAX、MIN)计算每组的统计值。HAVING 用于对聚合后的结果进行过滤,与 WHERE 的区别在于 HAVING 作用于分组后。
常见错误: 在
GROUP BY 查询中,SELECT 子句只能包含分组字段和聚合函数。例如 SELECT user_id, age, AVG(loan_amount) 如果 user_id 不是分组字段且 age 未聚合,会报错或返回随机值。
-- 按收入等级分组,计算各组的平均信用分与坏账率
SELECT
income_level,
COUNT(*) AS user_count,
AVG(credit_score) AS avg_credit_score,
SUM(CASE WHEN is_default = 1 THEN 1 ELSE 0 END) * 1.0 / COUNT(*) AS bad_rate
FROM credit_user
WHERE application_date >= '2024-01-01'
GROUP BY income_level
HAVING COUNT(*) >= 100 -- 只保留样本量足够的组
ORDER BY bad_rate DESC;
| 子句 | 执行顺序 | 过滤时机 | 能否使用聚合函数 |
|---|---|---|---|
| WHERE | 第2步(FROM之后) | 分组之前 | 不能 |
| HAVING | 第5步(GROUP BY之后) | 分组之后 | 可以 |
3.5 ORDER BY:排序输出
ORDER BY 对最终结果集按指定字段排序,ASC 升序(默认),DESC 降序。在信用评分A/B测试中,常用于输出风险最高的用户列表或按实验组排序对比。
-- 输出信用分最低的100个用户,用于人工审核
SELECT
user_id,
credit_score,
income_level,
is_default
FROM credit_user
WHERE application_date >= '2024-01-01'
ORDER BY credit_score ASC, is_default DESC
LIMIT 100;
3.6 核心语法执行顺序
理解SQL各子句的逻辑执行顺序,对写出高效、正确的查询至关重要。以下是SQL查询的完整执行顺序:
实战要点: 由于
WHERE 在 GROUP BY 之前执行,应尽量在 WHERE 中过滤掉无关数据,减少分组计算的数据量,提升查询性能。在信用评分大数据场景下,这一优化可节省数分钟的计算时间。
3.7 信用评分A/B测试综合查询示例
将上述语法综合运用,完成一个典型的A/B测试效果评估查询:对比实验组(新评分模型)与对照组(旧评分模型)在相同时间窗口内的逾期率差异。
-- A/B测试效果评估:实验组 vs 对照组逾期率对比
SELECT
experiment_group,
COUNT(*) AS total_users,
SUM(is_default_90dpd) AS default_count,
ROUND(SUM(is_default_90dpd) * 100.0 / COUNT(*), 2) AS bad_rate_pct,
AVG(credit_score) AS avg_score,
AVG(loan_amount) AS avg_loan
FROM credit_ab_test
WHERE test_date BETWEEN '2024-04-01' AND '2024-06-30'
AND experiment_group IN ('treatment', 'control')
GROUP BY experiment_group
HAVING COUNT(*) >= 500
ORDER BY bad_rate_pct ASC;
输出结果解读: 若实验组(treatment)的
bad_rate_pct 显著低于对照组(control),且样本量足够大,则说明新评分模型在风险区分能力上优于旧模型,可考虑全量上线。