最近遇到这样一个事:许多DBA的小伙伴不太明白金融业务中复借用户的定义方式以及sql查询方式,给他们提需求也整不明白,需求如下:
根据订单编号,查询用户该订单所对应的复借次数(白话讲就是:我有一笔订单,想看看这笔订单是用户在本平台的第几笔订单)
既然他们整不明白,那就自己写吧。
下面简单介绍下“复借”,再列举几种在mysql、oracle数据库中统计用户复借次数的方式。sql能力就是一点点提升的,希望能够抛砖引玉,窥一斑而知全豹。
复借是指在一个贷款平台借款订单还清后继续申请贷款的行为
复借次数即为该用户累计成功借还款的总次数
在数据库中,统计复借这个指标,用到的基本为还款表:即每个用户(user_id)的每一笔订单(apply_id)对应的还款情况(是否到期,是否成功还款)。
基本思路:成功还款1次,复贷次数+1
另外,考虑产品记期方式不同,我们分单期和多期两种产品来讨论
简单的说,就是两张相同的表做左外联,统计user_id相同时,第一张表apply_id比第二张表apply_id大的数据
内联子查询:生成两张相同的表,然后做左外联,这样表2中与表1user_id相同的数据就都会被查询得到,结果如下:
再用where做一层判断,把表1比表2apply_id大的数据筛选出来。效果如下:
:
对应的sql大致如下:
select * from
(SELECT * FROM repayment ) a
left join
(SELECT * FROM repayment ) b
on a.user_id = b.user_id and a.apply_id > b.apply_id
接下来分组统计apply_id计数,即可求得复借次数,下面为实操sql:
select a.user_id,a.apply_id,count(b.apply_id) loan_n
from
(SELECT r.user_id,r.apply_id
FROM`repayment` r
LEFT JOIN app_user u ON r.user_id = u.id
WHERE `repay_status` = '已还'
) a
left join
(SELECT r.user_id,r.apply_id
FROM`repayment` r
LEFT JOIN app_user u ON r.user_id = u.id
WHERE `repay_status` = '已还'
) b
on a.user_id = b.user_id and a.apply_id > b.apply_id
group by a.user_id,a.apply_id
ORDER BY a.user_id,loan_n
结果如下:
Oracle较mysq而言,增加了许多高级函数,常用的分析函数如下:
OVER(PARTITION BY… ORDER BY…)的使用说明和意义:
顾名思义,PARTITION 中文是分割的意思,ORDER 是排序的意思,所以翻译一下就是先把一组数据按照指定的字段进行分割成各种组,然后组内按照某个字段排序。
那我们大致的思路,就是先按照user_id对用户订单进行分组,再按照apply_id的大小进行一个正序排序,就是这么简单。
select
user_id,
apply_id,
row_number() over(partition by user_id order by apply_id)-1 loan_n
from repayment
和mysql处理后的效果一样
Mysql暂不支持lag()、lead()等统计函数,我们用自定义变量的方法来实现这个功能。
大致思路为: 固定列A(user_id列),定义两个自变量B(@user_id)、C(@rank),sql执行过程中动态比较A与B(注意细节,实际比的是A(n)与B(n-1)),如果一致,变量C(n)计数+1;如果不一致,变量C(n)重置为0。
也就是,sql按行执行的过程中,累计统计user_id为相同值的数量(一般实现排序功能,都这么用)
如下图,第一行user_id为4,@user_id(自定义变量)为空,rank就为0;第二行user_id为4,@user_id已更新为4,两数对比相同,rank就+1为1;第三行user_id为4,@user_id更新为4,两数对比相同,rank就+1为2;第四行user_id为21,@user_id更新为4,两数对比不同,rank重置为0。以此类推
sql实现步骤如下:
select
b.user_id,
b.apply_id,
if(@user_id = b.user_id, @rank := @rank , @rank := 0) as loan_n,@user_id:=b.user_id
from
(SELECT
r.user_id,
r.apply_id,
@user_id := null,
@rank := 0
#count(apply_id) loan_n
FROM`repayment` r
LEFT JOIN app_user u ON r.user_id = u.id
WHERE `repay_status` = '已还'
GROUP BY r.user_id,r.apply_id
order by user_id,r.apply_id
)b
效果如下:
较子查询而言,这种方法执行效率更高。
多期产品需要注意的地方是:
每一个订单对应着不同期数的还款记录。比如:6期的产品,就有6行贷后数据。我们要先把单笔借款的最后一期还款记录单独提取后再执行如单期产品相同的sql即可。
今天,我们针对一个简单的金融场景需求(统计复借次数)做了一些简单sql的尝试,主要成果如下:
其实,sql能力的加强,就在于有效处理这种看起来简单实际上复杂的问题,一次次的训练,一次次的提升。
对数据分析、机器学习、数据科学、金融风控等感兴趣的小伙伴,需要数据集、代码、行业报告等各类学习资料,可添加微信:wu805686220(记得要备注喔!),也可关注微信公众号:风控圏子(别打错字,是圏子,不是圈子,算了直接复制吧!)
关注公众号后,可联系圈子助手加入如下社群:
相互学习,共同成长。