3. SQL基础回顾:SELECT、WHERE、JOIN、GROUP BY、HAVING、窗口函数在风控中的应用场景
在实时风控巡检中,SQL 是提取、聚合、比对异常交易的核心工具。本节将回顾六类最常用的 SQL 语法,并直接对应到风控场景中的典型问题,如“查询高频交易”、“关联黑名单”、“按用户分组统计异常次数”、“筛选超过阈值的群体”以及“计算滑动窗口内的交易金额排名”。
3.1 SELECT & WHERE:基础过滤与异常定位
SELECT 指定要返回的字段,WHERE 对原始行进行条件过滤。在风控中,这是最频繁使用的组合,用于快速定位可疑交易。
| 风控场景 | SQL 条件示例 | 说明 |
|---|---|---|
| 大额交易预警 | WHERE amount > 100000 |
筛选单笔金额超过 10 万的交易 |
| 异地登录检测 | WHERE city != '北京' |
交易城市与注册城市不一致 |
| 夜间交易监控 | WHERE hour(trans_time) BETWEEN 0 AND 5 |
凌晨 0 点到 5 点的交易 |
| 新设备首次交易 | WHERE device_first_use = 1 |
设备首次出现在交易记录中 |
避坑提示: 在
WHERE 中对索引字段使用函数(如 HOUR(trans_time))会导致索引失效。建议在实时风控中,将时间字段拆分为 trans_date 和 trans_hour 两个字段,或使用 BETWEEN 直接比较时间戳范围。
-- 示例:查询今日单笔金额超过 5 万且交易失败的记录
SELECT
trans_id,
user_id,
amount,
trans_time,
status
FROM transaction_log
WHERE trans_date = CURRENT_DATE
AND amount > 50000
AND status = 'FAILED';
3.2 JOIN:关联多表构建风控特征
风控数据通常分散在多个表中:交易流水表、用户信息表、设备指纹表、黑名单表。JOIN 将这些表关联起来,形成完整的风险画像。
| JOIN 类型 | 风控应用场景 | 示例 |
|---|---|---|
| INNER JOIN | 匹配交易与用户信息,只返回有完整用户数据的交易 | 交易表 JOIN 用户表 ON user_id |
| LEFT JOIN | 保留所有交易,补充设备信息(可能为空) | 交易表 LEFT JOIN 设备表 ON device_id |
| ANTI JOIN(NOT EXISTS) | 查找不在白名单中的交易 | 交易表 WHERE NOT EXISTS (白名单) |
| SEMI JOIN(EXISTS) | 查找在黑名单中的用户交易 | 交易表 WHERE EXISTS (黑名单) |
-- 示例:关联黑名单表,查询命中黑名单用户的交易
SELECT
t.trans_id,
t.user_id,
t.amount,
b.black_reason
FROM transaction_log t
INNER JOIN blacklist b ON t.user_id = b.user_id
WHERE t.trans_date = CURRENT_DATE;
性能建议: 在实时风控中,
JOIN 操作尽量使用小表驱动大表。黑名单表通常较小,应作为驱动表放在 FROM 子句或使用 IN 子查询替代 JOIN,以减少扫描行数。
3.3 GROUP BY & HAVING:聚合统计与群体异常筛选
GROUP BY 按维度(用户、设备、IP)聚合数据,HAVING 对聚合后的结果进行过滤。这是识别“群体性异常”的关键手段,例如同一 IP 下多个账户同时交易。
| 聚合维度 | 统计指标 | HAVING 条件 | 风控含义 |
|---|---|---|---|
| 用户 | COUNT(trans_id) | HAVING cnt > 10 | 单日交易次数超过 10 次 |
| IP 地址 | COUNT(DISTINCT user_id) | HAVING user_cnt > 5 | 同一 IP 关联超过 5 个用户 |
| 设备 | SUM(amount) | HAVING total_amount > 500000 | 单设备累计交易额超过 50 万 |
| 银行卡 | COUNT(DISTINCT device_id) | HAVING device_cnt > 3 | 同一银行卡绑定超过 3 台设备 |
-- 示例:按 IP 分组,找出今日关联用户数超过 3 个的异常 IP
SELECT
ip_address,
COUNT(DISTINCT user_id) AS user_cnt,
COUNT(trans_id) AS trans_cnt
FROM transaction_log
WHERE trans_date = CURRENT_DATE
GROUP BY ip_address
HAVING COUNT(DISTINCT user_id) > 3;
注意:
WHERE 在 GROUP BY 之前过滤行,HAVING 在 GROUP BY 之后过滤组。不要在 HAVING 中使用非聚合字段,应将其移到 WHERE 子句中以提高性能。
3.4 窗口函数:滑动窗口内的排名与累计计算
窗口函数在不改变行数的情况下,为每一行计算其所在窗口内的排名、累计值、前后行差值。在实时风控中,窗口函数用于检测“短时间内连续交易”、“金额突增”等时序异常。
| 窗口函数 | 风控应用场景 | 窗口定义 |
|---|---|---|
| ROW_NUMBER() | 按时间排序,标记用户第几笔交易 | PARTITION BY user_id ORDER BY trans_time |
| LAG() / LEAD() | 对比当前交易与上一笔交易的时间间隔、金额变化 | LAG(trans_time) OVER (PARTITION BY user_id ORDER BY trans_time) |
| SUM() OVER | 计算过去 1 小时内用户的累计交易金额 | SUM(amount) OVER (PARTITION BY user_id ORDER BY trans_time RANGE INTERVAL '1' HOUR PRECEDING) |
| RANK() | 按交易金额排名,找出每个用户的前 3 大交易 | RANK() OVER (PARTITION BY user_id ORDER BY amount DESC) |
-- 示例:使用 LAG 计算同一用户相邻两笔交易的时间间隔(秒)
SELECT
trans_id,
user_id,
trans_time,
LAG(trans_time) OVER (
PARTITION BY user_id
ORDER BY trans_time
) AS prev_trans_time,
EXTRACT(EPOCH FROM (trans_time - LAG(trans_time) OVER (
PARTITION BY user_id
ORDER BY trans_time
))) AS interval_seconds
FROM transaction_log
WHERE trans_date = CURRENT_DATE;
实时风控技巧: 将窗口函数与
WHERE 子查询结合,可以筛选出“1 分钟内交易超过 3 笔”的用户。先使用窗口函数计算每笔交易的前后时间差,再在外层过滤 WHERE interval_seconds < 60,配合 COUNT(*) OVER 统计连续交易次数。
3.5 六类 SQL 在风控中的协同关系
下图展示了六类 SQL 语法在实时风控巡检中的典型调用链路:从原始交易数据开始,经过过滤、关联、聚合、窗口计算,最终输出异常交易名单。
3.6 综合案例:实时风控巡检 SQL 脚本
以下脚本将上述六类语法组合使用,实现一个完整的实时风控巡检逻辑:筛选今日交易、关联黑名单、按 IP 聚合、使用窗口函数计算时间间隔,最终输出高风险交易。
-- 实时风控巡检综合脚本
WITH filtered_trans AS (
-- 1. SELECT + WHERE:过滤今日交易且金额大于 1000
SELECT *
FROM transaction_log
WHERE trans_date = CURRENT_DATE
AND amount > 1000
),
joined_data AS (
-- 2. JOIN:关联黑名单表
SELECT
f.*,
b.black_reason,
b.black_level
FROM filtered_trans f
LEFT JOIN blacklist b ON f.user_id = b.user_id
),
agg_ip AS (
-- 3. GROUP BY + HAVING:按 IP 聚合,筛选关联用户数超过 3 的 IP
SELECT
ip_address,
COUNT(DISTINCT user_id) AS user_cnt,
COUNT(trans_id) AS trans_cnt
FROM joined_data
GROUP BY ip_address
HAVING COUNT(DISTINCT user_id) > 3
),
window_rank AS (
-- 4. 窗口函数:按用户分组,按时间排序,标记每笔交易的序号
SELECT
j.*,
ROW_NUMBER() OVER (
PARTITION BY j.user_id
ORDER BY j.trans_time
) AS trans_seq,
LAG(j.trans_time) OVER (
PARTITION BY j.user_id
ORDER BY j.trans_time
) AS prev_trans_time
FROM joined_data j
WHERE j.ip_address IN (SELECT ip_address FROM agg_ip)
)
-- 5. 最终输出:高风险交易
SELECT
trans_id,
user_id,
amount,
trans_time,
ip_address,
black_reason,
trans_seq,
EXTRACT(EPOCH FROM (trans_time - prev_trans_time)) AS interval_sec
FROM window_rank
WHERE black_level IS NOT NULL -- 命中黑名单
OR trans_seq >= 5 -- 同一用户今日第 5 笔及以上交易
OR interval_sec < 10; -- 与上一笔交易间隔小于 10 秒
关键总结: 在实时风控巡检中,SQL 的六类语法不是孤立使用的。
SELECT + WHERE 做第一层过滤,JOIN 引入外部风险特征,GROUP BY + HAVING 发现群体异常,窗口函数 捕捉时序突变。掌握这六类语法的组合使用,是构建高效风控 SQL 脚本的基础。