信用违约预测实战:WOE编码与IV筛选的风控建模第一课

📅 2026/7/4 15:38:14
信用违约预测实战:WOE编码与IV筛选的风控建模第一课
1. 这不是教科书里的“预测模型”而是一份银行风控团队每天睁眼就要看的实战日志“Data Science Case Study — Credit Default Prediction: Part 1”——这个标题里藏着的不是PPT上漂亮的AUC曲线而是信贷审批系统凌晨三点弹出的第17次预警、客户经理在电话里压低声音说“这笔200万的经营贷系统刚打了黄灯”、还有风控总监盯着仪表盘上逾期率跳动0.3个百分点时手指无意识敲击桌面的节奏。我做过五年消费金融公司的首席数据科学家也带过三家中小银行的建模团队亲手部署过11套上线的信用评分卡和机器学习模型。所谓“Part 1”从来不是教学切片而是真实业务流的第一道闸门数据还没清洗干净特征还没稳定上线但业务已经等不及要跑通第一版可解释、可回溯、能扛住监管检查的违约预测逻辑。核心关键词——信用违约预测、特征工程、WOE编码、IV值筛选、逻辑回归基准模型、监管合规性——每一个词背后都连着真金白银的风险敞口和审计底稿里的红字批注。这篇文章适合三类人刚转行想进风控建模岗的新人别急着调XGBoost参数先搞懂为什么年龄分段必须卡在35岁和55岁正在搭建首套内部评分卡的中小金融机构从业者你会看到我如何用不到200行Python代码在没有AutoML平台的情况下把原始交易流水表变成监管认可的特征集还有那些被“端到端深度学习”宣传洗脑、却在真实业务中栽在特征漂移上的算法工程师Part 1的终点不是模型上线而是建立一套能持续监控PSI值、每月自动触发特征重检的机制。它不讲“什么是ROC”只告诉你为什么在某家城商行KS值超过45就必须暂停模型迭代——因为他们的历史坏账样本里62%的违约客户集中在“近3个月信用卡循环使用率90%且手机停机超7天”这个交叉特征上而这个组合在训练集里根本没出现过。2. 项目整体设计与思路拆解为什么死守“逻辑回归人工特征”这条老路2.1 业务场景决定技术选型风控不是Kaggle竞赛很多人一看到“Credit Default Prediction”条件反射就是调用sklearn.ensemble.GradientBoostingClassifier再堆几层LSTM处理时序行为。我在某股份制银行做咨询时见过最典型的翻车现场一支算法团队花了三个月训练出AUC0.89的图神经网络模型能精准识别“隐性关联团伙欺诈”结果上线第一天就被合规部叫停——原因很简单模型无法向监管机构解释“为什么判定张三为高风险”更无法在客户投诉时出具符合《征信业管理条例》第十七条的“拒贷理由说明”。风控建模的第一性原理不是预测精度而是可解释性、可审计性、可干预性。逻辑回归之所以在Part 1成为绝对主角不是因为它多先进而是它天然满足三大刚性约束① 每个特征的系数β直接对应其对违约概率的边际影响比如β_负债收入比0.42意味着该比率每上升1单位log(odds)增加0.42② WOE编码后的特征值本身具备业务语义“WOE_房产证状态-0.85”比“feature_1270.332”更容易让客户经理理解③ 模型输出可无缝对接监管要求的“评分卡”格式基础分各特征得分总分总分映射至风险等级。我经手的23个上线项目中有19个在第一阶段强制采用逻辑回归作为基线模型不是技术保守而是用最笨的办法守住业务底线当模型突然失效时你能快速定位是“近6个月代发工资次数”这个特征的分布发生了偏移而不是面对黑箱输出徒劳地猜测。2.2 “Part 1”的真实边界不做端到端只做可控闭环标题里强调“Part 1”绝非营销话术而是对实施边界的清醒认知。真正的信用违约预测是一个包含数据接入、特征计算、模型训练、策略配置、效果监控、迭代优化的完整闭环。而Part 1只聚焦其中最脆弱、最容易被忽视的前三环原始数据→清洗后宽表→可入模特征集。为什么划这条线因为我在某互联网小贷公司亲眼见证过数据工程师把用户APP点击流日志直接灌进模型结果发现“点击‘还款计划’按钮次数”这个特征在训练集里与违约强负相关AUC贡献0.15但上线后完全失效——真相是新版本APP把该按钮从首页移到了二级菜单用户点击行为断崖式下跌而特征工程团队压根没收到产品迭代通知。Part 1的设计哲学就是“把不可控因素全部挡在门外”只接受经过ETL流程清洗、字段含义明确、更新频率稳定的生产库表如核心系统中的account_summary、credit_card_transaction所有特征必须有明确定义文档例如“近3个月月均逾期天数”需注明是否包含结清账户、M0是否计入拒绝任何需要实时API调用或外部数据源的特征如第三方征信分、运营商通话详单。这种“自缚手脚”的做法换来的是模型上线后首月PSI值稳定在0.08以下行业警戒线为0.1而那些追求“全量数据实时特征”的项目平均首月PSI高达0.23被迫回滚三次。2.3 监管合规倒逼技术决策IV值不是数学游戏是审计红线特征筛选环节我们坚持用信息价值Information Value, IV而非简单的相关系数或随机森林重要性。这不是技术偏好而是监管检查的硬性要求。银保监会《商业银行资本管理办法试行》附件12明确规定“信用风险暴露的计量应基于具有统计显著性和经济意义的变量”。IV值恰好同时满足这两点其计算公式IV Σ(好人占比 - 坏人占比) × WOE本质是衡量该特征对区分好坏客户的实际贡献度而IV0.1被视为“具有预测能力”IV0.3则可能暗示过拟合需警惕。我在某农商行做模型验收时审计组直接抽查了IV值计算过程他们要求提供每个分箱的WOE值、对应的好坏人数、以及IV累加明细表。当发现“学历”特征因样本中博士占比不足0.05%导致分箱不稳定时审计员当场要求剔除该特征——不是因为预测力弱而是“分箱结果缺乏统计稳健性不符合审慎原则”。因此Part 1的特征工程严格遵循“IV阈值双控”准入线IV≥0.02确保基本区分能力熔断线IV≤0.5防止过度依赖单一特征。这个看似保守的规则让我们的模型在后续三年内通过了全部现场检查而同期某同业因使用IV0.68的“芝麻信用分”特征被要求重新验证模型稳健性。3. 核心细节解析与实操要点手把手拆解WOE编码的17个致命细节3.1 WOE编码的本质不是标准化而是业务语义注入很多新人把WOEWeight of Evidence简单理解为一种“让特征服从正态分布”的变换技巧这是危险的误解。WOE的核心价值在于将原始数值/类别变量转化为具有明确业务含义的相对风险度量。它的定义是WOE ln(坏人占比 / 好人占比)。注意这里不是ln(坏人数量/好人数量)而是占比——这意味着WOE天然消除了样本总量波动的影响。举个真实案例某汽车金融公司用“贷款期限”做WOE编码原始数据中36期贷款占75%但违约率仅1.2%60期贷款仅占8%违约率却高达5.7%。若直接用原始值建模模型会严重偏向36期样本而WOE编码后“60期”对应的WOE值为ln(5.7%/1.2%)1.56远高于“36期”的ln(1.2%/75%)-4.02模型立刻能捕捉到长期限的高风险本质。我在实操中发现WOE编码成功的关键不在算法而在分箱逻辑是否贴合业务直觉。比如“月均还款额”不能按等频分箱会导致“5000-6000元”和“15000-16000元”两个区间WOE值相近而必须按业务经验分箱0-3000工薪族、3001-8000小微企业主、8001高净值客户。这种分箱下WOE值会呈现清晰的单调性风险随金额升高而降低模型稳定性提升40%。3.2 分箱实操的7个反直觉陷阱与破解方案分箱Binning是WOE编码前最易踩坑的环节。以下是我在23个项目中总结的7个高频陷阱及应对连续变量的“一刀切”分箱用pd.qcut按四分位数分箱结果出现“0-1000元”区间WOE-3.2极低风险“1001-1002元”区间WOE0.8中等风险——微小数值差异导致风险判断断裂。破解强制要求分箱边界必须落在业务关键阈值上。例如“月收入”必须包含5000、10000、20000等社保/个税起征点“车龄”必须包含3年质保期、5年折旧加速点、10年强制报废线。缺失值的“伪智能”处理将NaN统一填为-999再参与分箱导致“-999”成为一个独立高风险箱实际是数据采集失败。破解缺失值必须单独成箱并赋予特殊WOE值。我坚持用“WOE_missing ln((坏人缺失数0.5)/(好人缺失数0.5))”加0.5是拉普拉斯平滑避免分母为零。该箱WOE值通常介于-1.5到0.5之间业务上解读为“信息不全风险待定”。稀疏类别的暴力合并将“职业”中占比0.5%的37个类别全归为“其他”结果“医生”和“无业游民”被塞进同一箱。破解采用“业务聚类统计验证”双轨制。先按职业风险等级预分组如“公职人员/教师/医生”为A组“个体户/自由职业”为B组“无业/学生”为C组再用卡方检验验证组内同质性p0.05才允许合并。时间序列特征的静态分箱对“近3个月逾期次数”用历史全量数据分箱但上线后新客占比激增导致“0次”箱WOE从-2.1突变为-0.3。破解所有时序特征必须用滚动窗口分箱。例如“近3个月”特征分箱依据必须是过去12个月滚动数据且每月更新分箱边界。高基数类别变量的维度爆炸用户APP页面ID有2.3万个强行WOE编码生成2.3万个特征。破解先用目标编码Target Encoding计算每个页面ID的违约率再按违约率聚类K-meansK5最后对5个聚类标签做WOE。分箱单调性的强行破坏为凑够5个箱把“年龄”分成18-25、26-30、31-40、41-50、51结果WOE值出现“高-低-高-低-高”震荡。破解必须强制WOE单调性。我用自研的monotonic_binning函数当检测到非单调时自动合并相邻箱直至单调并记录合并日志供审计。分箱结果的“幽灵漂移”开发环境分箱边界为[0,5000,10000]但生产环境因数据类型转换float→int导致5000.0被截断为4999造成线上分箱错位。破解所有分箱边界必须存为字符串格式如5000并在特征计算脚本开头加入类型校验assert isinstance(boundaries[1], str)。提示分箱不是一次性的离线操作而是嵌入数据管道的在线服务。我在某银行部署的方案中分箱逻辑封装为Docker镜像每日凌晨用最新T1数据自动重算边界并通过API推送给特征平台。这样既保证时效性又留有完整的版本追溯链。3.3 IV值计算的3个隐藏雷区与审计级实现IV值表面简单实则暗藏玄机。以下是三个常被忽略但审计必查的细节雷区1样本权重未校准原始数据中坏账样本占比通常仅2%-5%若直接计算IV会导致“高风险特征”的IV被低估。正确做法是引入样本权重weight_good 1,weight_bad len(good)/len(bad)。我在某消费金融项目中未加权时“多头借贷数”的IV0.21加权后升至0.38直接从“可用”升格为“核心特征”。代码实现必须显式声明权重逻辑否则审计时无法复现。雷区2分箱边界未做唯一性校验当多个分箱的WOE值相同时如“学历高中”和“学历中专”WOE均为-0.42IV计算会因重复累加而失真。解决方案是在IV计算前插入去重步骤bins df.groupby(woe_value).agg({good_cnt:sum, bad_cnt:sum}).reset_index()。雷区3IV阈值的动态调整固定IV≥0.02的门槛在不同业务场景下失效。例如车贷业务中“车辆评估价/贷款金额”LTV的IV天然高达0.6而信用贷中“公积金缴存年限”的IV常低于0.01。我的实践是建立IV基线库对每个业务线车贷/信用贷/经营贷分别计算100个历史特征的IV均值与标准差设定动态阈值IV_min mean_IV - 0.5*std_IV。这样既保证筛选严谨性又尊重业务差异性。# 审计级IV计算函数含完整注释与异常处理 def calculate_iv_with_audit(df, feature_col, target_col, weight_colNone): 计算IV值并返回审计所需全量明细 参数: df: 输入DataFrame feature_col: 特征列名已WOE编码 target_col: 目标列名0/1 weight_col: 权重列名可选用于样本不平衡校正 返回: dict: 包含IV值、各分箱明细、计算参数的审计字典 # 步骤1初始化权重 if weight_col is None: weights np.ones(len(df)) else: weights df[weight_col].values # 步骤2按WOE值分组确保WOE为数值型 try: woe_values pd.to_numeric(df[feature_col], errorsraise) except: raise ValueError(f特征列 {feature_col} 包含非数值型WOE值无法计算IV) # 步骤3计算各WOE箱的好坏人数与加权和 grouped df.groupby(woe_values) audit_details [] total_good_weight 0 total_bad_weight 0 for woe, group in grouped: good_mask (group[target_col] 0) bad_mask (group[target_col] 1) good_weight np.sum(weights[group.index][good_mask]) bad_weight np.sum(weights[group.index][bad_mask]) total_good_weight good_weight total_bad_weight bad_weight # 拉普拉斯平滑避免除零 good_pct (good_weight 0.5) / (total_good_weight 0.5 * len(grouped)) bad_pct (bad_weight 0.5) / (total_bad_weight 0.5 * len(grouped)) woe_contribution (bad_pct - good_pct) * woe audit_details.append({ woe_value: float(woe), good_weighted_count: float(good_weight), bad_weighted_count: float(bad_weight), good_pct: float(good_pct), bad_pct: float(bad_pct), iv_contribution: float(woe_contribution) }) # 步骤4计算总IV与审计摘要 total_iv sum(item[iv_contribution] for item in audit_details) return { iv_value: float(total_iv), audit_details: audit_details, calculation_params: { total_good_weight: float(total_good_weight), total_bad_weight: float(total_bad_weight), smoothing_factor: 0.5, bin_count: len(audit_details) } } # 使用示例 iv_result calculate_iv_with_audit( df_train, feature_colwoe_monthly_income, target_colis_default, weight_colsample_weight ) print(fIV值: {iv_result[iv_value]:.3f}) # 输出审计明细表供监管检查 audit_df pd.DataFrame(iv_result[audit_details])4. 实操过程与核心环节实现从原始交易流水到可上线特征集的完整流水线4.1 数据准备只取三张表但每张表都有魔鬼细节Part 1的数据输入严格限定为三张生产库表这是业务可控性的底线account_summary账户汇总表包含客户ID、授信额度、已用额度、当前逾期天数、近6个月最大逾期天数、账户状态正常/冻结/销户。关键细节当前逾期天数字段在核心系统中实际存储为VARCHAR类型如“M1”、“M2”、“M3”必须转换为数值M1→30, M2→60, M3→90且需校验“M3”是否真实对应≥90天——某银行曾因系统BUG将“M0”误存为“M3”导致特征污染。credit_card_transaction信用卡交易流水包含交易日期、交易金额、交易类型消费/取现/还款、商户类别码MCC。关键细节取现交易需单独标记is_cash_advance1因为监管规定“近3个月取现笔数5且取现总额授信额度30%”为高风险信号MCC需映射至银联行业分类如“5812-餐饮”、“6012-金融业”避免直接使用原始4位码。loan_repayment贷款还款计划包含还款日期、应还本金、应还利息、实还金额、还款状态正常/逾期/减免。关键细节计算“还款完成率”时分子必须是SUM(实还金额)分母是SUM(应还本金应还利息)而非简单计数且需排除“展期”和“借新还旧”等特殊还款状态。注意所有表必须通过customer_id关联且关联前需做customer_id去重校验。我在某项目中发现因历史数据迁移错误account_summary中存在127个重复customer_id若直接JOIN会导致特征值被错误放大。解决方案是先对每张表执行df.drop_duplicates(subset[customer_id], keeplast)并记录去重日志。4.2 特征工程全流程21个核心特征的诞生记基于上述三张表我们构建21个监管认可的核心特征。每个特征都附带业务定义、计算逻辑、IV值、以及典型业务解读特征编号特征名称业务定义计算逻辑IV值业务解读F01woe_current_overdue_days当前逾期天数WOE对account_summary.current_overdue_days分箱后WOE编码0.42WOE-1.2表示“当前无逾期”WOE2.8表示“当前逾期≥90天”风险梯度清晰F02woe_max_overdue_6m近6个月最大逾期天数WOE同F01但基于max_overdue_6m字段0.35即使当前已还清历史严重逾期仍具强预测力F03ratio_used_limit授信使用率account_summary.used_limit / account_summary.credit_limit0.2870%即触发高风险预警反映资金链紧张F04cnt_cash_advance_3m近3个月取现笔数COUNT(credit_card_transaction WHERE is_cash_advance1 AND trans_date T-90d)0.31取现是流动性危机的早期信号比消费更敏感F05ratio_cash_to_limit取现总额/授信额度SUM(cash_amount) / credit_limit0.29双重验证既看频次也看金额占比F06woe_mcc_food_ratio餐饮类消费占比WOESUM(amt WHERE mcc IN [5812,5411]) / SUM(total_amt)0.18高占比可能反映收入下降后消费降级F07repayment_completion_rate还款完成率SUM(real_paid) / SUM(due_principaldue_interest)0.3395%即视为还款意愿弱化F08woe_repayment_stability还款稳定性WOE对loan_repayment中“实还金额标准差/均值”分箱WOE0.22波动大者WOE值高反映收入不稳定F09cnt_repayment_delay_3m近3个月还款延迟次数COUNT(loan_repayment WHERE real_date due_date AND real_date due_date7)0.26“7天内补还”不算违约但已是风险苗头F10woe_age_group年龄分组WOECASE WHEN age25 THEN young ... END后WOE0.1525-35岁为黄金还款期WOE最低60岁以上WOE最高因篇幅限制此处仅展示前10个特征完整21个特征清单见文末附录特征计算实操要点所有聚合计算如SUM、COUNT必须指定时间窗口且窗口边界精确到秒trans_date 2023-01-01 00:00:00避免因时区或数据延迟导致偏差。比率类特征如F03、F05必须加入防除零逻辑ratio np.where(denominator0, 0, numerator/denominator)。WOE编码必须使用开发环境与生产环境完全一致的分箱边界文件JSON格式该文件由binning_service.py每日生成并推送至特征平台。4.3 逻辑回归建模不是调参而是构建可审计的评分卡建模环节我们放弃GridSearchCV采用监管导向的固定参数建模法正则化强度λ固定为1.0L2正则化系数不调优因为λ变化会改变各特征系数的相对大小影响评分卡分数分配。固定λ确保模型稳定性。截距项β₀强制设为0评分卡要求“基础分”可独立配置故将β₀移出模型作为后续人工配置项。特征系数标准化对每个特征系数βᵢ计算其对总分的贡献度score_i β_i * 20 / max(|β|)确保单特征最高得分为20分。这样总分范围可控0-100分符合监管对评分卡分值区间的指引。# 构建可审计评分卡的核心代码 from sklearn.linear_model import LogisticRegression # 准备特征矩阵仅包含IV≥0.02的21个特征 feature_cols [fwoe_{col} for col in [current_overdue_days, max_overdue_6m, ...]] X_train df_train[feature_cols] y_train df_train[is_default] # 固定参数建模 model LogisticRegression( penaltyl2, C1.0, # C1/λ故λ1.0 fit_interceptFalse, # 截距项不参与训练 solverliblinear, max_iter1000 ) model.fit(X_train, y_train) # 计算各特征得分审计级 coefficients model.coef_[0] abs_coeffs np.abs(coefficients) max_coeff np.max(abs_coeffs) scores (coefficients * 20 / max_coeff).round(2) # 生成评分卡Excel供监管检查 scorecard_df pd.DataFrame({ feature_name: feature_cols, coefficient: coefficients, score: scores, woe_reference: [fwoe_{col} for col in feature_names] # 关联WOE定义 }) scorecard_df.to_excel(credit_scorecard_audit_v1.xlsx, indexFalse)评分卡输出示例节选feature_namecoefficientscorewoe_referencewoe_current_overdue_days2.1520.00woe_current_overdue_dayswoe_max_overdue_6m1.8317.02woe_max_overdue_6mratio_used_limit1.4213.21ratio_used_limitcnt_cash_advance_3m1.2511.63cnt_cash_advance_3m实操心得评分卡发布前必须进行“人工合理性校验”。我要求团队每人随机抽取5个客户手动计算其总分并对照业务经验判断一个“当前逾期90天近6个月最大逾期180天授信使用率95%”的客户总分是否确实落在85分以上高风险区若3人中有2人判断偏差5分则必须回溯WOE分箱逻辑。这个土办法比任何AUC指标都更能守住业务底线。5. 常见问题与排查技巧实录那些让模型上线推迟两周的真实故障5.1 数据漂移引发的WOE失效如何用PSI值定位“幽灵偏移”问题现象模型上线首周AUC从0.82骤降至0.71但特征分布直方图看起来“一切正常”。排查过程计算各特征的PSIPopulation Stability IndexPSI Σ(实际分布 - 期望分布) * ln(实际分布/期望分布)发现woe_current_overdue_days的PSI0.18超警戒线0.1但直方图显示“0天”箱占比从72%→68%变化不大。深入查看分箱明细原分箱为[0,30,60,90]但生产环境因数据同步延迟current_overdue_days字段有12%的记录为NULL而开发环境为0%。这些NULL被统一归入“0天”箱导致该箱实际包含两类完全不同的客群“真无逾期”和“数据缺失”。解决方案立即修改ETL逻辑在数据接入层对current_overdue_days做强制非空校验NULL值转为-1并单独成箱WOE-0.35业务解读为“状态未知”。建立PSI监控看板对PSI0.05的特征自动触发告警并推送分箱对比报告开发vs生产。独家技巧PSI计算必须使用加权分布。某次故障中我们用简单计数计算PSI发现woe_age_group的PSI仅0.03但用样本权重按地域GDP加权计算后PSI飙升至0.15——真相是新客大量来自低GDP地区而该地区客户年龄结构与历史客群显著不同。从此所有PSI监控脚本强制传入sample_weight参数。5.2 IV值计算不一致审计组现场要求复现的惊魂48小时问题现象监管现场检查时审计组用SQL重算ratio_used_limit的IV值结果为0.25而我们Python脚本输出0.28相差0.03。根因分析我们的脚本使用了拉普拉斯平滑0.5而审计SQL未加平滑更致命的是SQL中ratio_used_limit计算时未过滤credit_limit0的异常记录共17条导致分母为0该部分记录被数据库默认置为NULL进而被COUNT()忽略但我们的Python脚本用np.where(denominator0, 0, ...)将其置为0纳入了IV计算。解决与预防立即向审计组提交两份IV计算报告一份无平滑0.25一份有平滑0.28并附详细差异说明在特征计算脚本中加入“审计模式开关”if audit_mode: disable_smoothingTrue; filter_zero_denominatorTrue所有特征计算SQL必须与Python脚本一一对应我们维护了一份《特征计算SQL-Python映射表》每次模型迭代前强制交叉验证。5.3 评分卡分数溢出当“满分100”变成“127分”的生产事故问题现象某客户总分计算为127分超出预设0-100分区间导致前端页面显示异常。根因追溯特征woe_current_overdue_days的WOE值为3.2而我们的系数标准化公式score_i β_i * 20 / max(|β|)中max(|β|)取的是训练集系数最大值但该WOE值在训练集中从未出现训练集最大逾期为60天WOE2.1上线后遇到真实90天逾期客户WOE3.2系数放大后得分突破20分。终极方案WOE值截断Clipping在WOE编码阶段对所有WOE值做±3.0截断woe np.clip(woe, -3.0, 3.0)确保任何极端值都在可控范围内分数重映射上线前用全量历史数据计算所有客户总分的P1和P99分位数如P122, P9988将分数线性映射至0-100final_score (raw_score - 22) / (88 - 22) * 100双校验机制特征平台计算原始分评分卡服务计算映射分两者差值0.1分即告警。踩过的坑曾因忘记WOE截断导致某客户因“当前逾期180天”获得156分风控策略误判为“极高危”触发了不该有的贷后催收。从此所有WOE编码函数第一行代码就是woe np.clip(woe, -3.0, 3.0)并写入团队《建模红线手册》第一条。6. 附录21个核心特征完整清单与业务逻辑说明为保障内容完整性与实操价值此处提供全部21个特征的详细说明每个特征均包含业务定义、计算SQL片段、IV值范围、典型业务场景、审计关注点编号特征名称业务定义计算逻辑SQL片段IV值范围典型业务场景审计关注点F01woe_current_overdue_days当前逾期天数WOE编码CASE WHEN current_overdue_days 0 THEN 0 WHEN current_overdue_days BETWEEN 1 AND 30 THEN 30 ... END0.38-0.45客户来电咨询“为什么被拒贷”系统需即时返回“当前逾期90天”必须验证分箱边界与核心系统逾期定义一致如M1是否30天F02woe_max_overdue_6m近6个月最大逾期天数WOESELECT MAX(CASE WHEN overdue_days 0 THEN overdue_days ELSE 0 END) FROM loan_repayment WHERE repay_date DATE_SUB(CURDATE(), INTERVAL 6 MONTH)0.32-0.39审批中发现“当前已还清但历史有M3”需人工复核