3. SQL环境准备:搭建测试用数据库与批次偏移模拟表

在进入PSI计算与监控SQL编写之前,必须先搭建一个可复现的测试环境。本章将指导你完成两项核心任务:创建测试数据库(MySQL / PostgreSQL 二选一)和设计批次偏移模拟表结构。该表将作为后续所有SQL脚本的数据源,其字段设计直接决定了PSI计算的准确性与可追溯性。

3.1 环境选择与数据库创建

本课程同时支持MySQL 8.0+ 和 PostgreSQL 15+,两种数据库在PSI计算逻辑上完全一致,仅在数据类型和窗口函数语法上有细微差异。请根据你的实际环境选择其一执行。

对比维度 MySQL 8.0+ PostgreSQL 15+
推荐场景 已有MySQL基础设施、团队熟悉MySQL 需要更严格的数据类型、复杂分析场景
数值精度 DECIMAL(38,10) 足够 NUMERIC(38,10) 更标准
窗口函数 支持,语法标准 支持,性能更优
JSON支持 JSON类型 JSONB类型(推荐)
推荐选择:如果你是初学者,建议使用 PostgreSQL,其数据类型更严格、错误提示更清晰,有助于培养规范的SQL编写习惯。本课程示例代码将同时标注两种数据库的语法差异。

3.1.1 创建测试数据库

使用以下SQL命令创建专用数据库。数据库名称建议使用 psi_monitor,便于后续章节统一引用。

-- MySQL 版本
CREATE DATABASE IF NOT EXISTS psi_monitor
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

USE psi_monitor;

-- PostgreSQL 版本
CREATE DATABASE psi_monitor
  WITH ENCODING 'UTF8'
  LC_COLLATE = 'en_US.UTF-8'
  LC_CTYPE = 'en_US.UTF-8';

\c psi_monitor;

3.2 批次偏移模拟表结构设计

批次偏移模拟表是PSI监控的核心数据载体,其设计需满足以下业务需求:

  • 按批次记录:每个模型或特征在每次评分时产生一条记录。
  • 特征级粒度:支持多特征同时监控,每个特征独立计算PSI。
  • 分箱统计:存储每个分箱的实际占比和预期占比,这是PSI计算的基础。
  • 可追溯性:能够回溯任意批次、任意特征的偏移情况。

3.2.1 表字段定义

字段名 数据类型(MySQL) 数据类型(PostgreSQL) 说明
batch_id VARCHAR(32) VARCHAR(32) 批次唯一标识,格式如 20250101 或 BATCH_2025_01_01
feature_name VARCHAR(128) VARCHAR(128) 特征名称,如 age、income_level、credit_score
feature_value VARCHAR(64) VARCHAR(64) 分箱标签,如 [0-30)、high、class_A
actual_count DECIMAL(38,10) NUMERIC(38,10) 当前批次该分箱的实际样本数(或占比)
expected_count DECIMAL(38,10) NUMERIC(38,10) 基准批次该分箱的预期样本数(或占比)
关键设计决策:actual_count 和 expected_count 使用高精度数值类型(38,10),而非整数类型。原因有二:一是PSI计算过程中需要频繁计算占比,使用小数可避免整数除法截断;二是某些场景下预期占比可能直接以百分比形式给出,小数类型更灵活。

3.2.2 创建表SQL

-- MySQL 版本
CREATE TABLE IF NOT EXISTS batch_psi_monitor (
    batch_id       VARCHAR(32)    NOT NULL,
    feature_name   VARCHAR(128)   NOT NULL,
    feature_value  VARCHAR(64)    NOT NULL,
    actual_count   DECIMAL(38,10) NOT NULL DEFAULT 0,
    expected_count DECIMAL(38,10) NOT NULL DEFAULT 0,
    PRIMARY KEY (batch_id, feature_name, feature_value),
    INDEX idx_batch_feature (batch_id, feature_name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- PostgreSQL 版本
CREATE TABLE IF NOT EXISTS batch_psi_monitor (
    batch_id       VARCHAR(32)    NOT NULL,
    feature_name   VARCHAR(128)   NOT NULL,
    feature_value  VARCHAR(64)    NOT NULL,
    actual_count   NUMERIC(38,10) NOT NULL DEFAULT 0,
    expected_count NUMERIC(38,10) NOT NULL DEFAULT 0,
    PRIMARY KEY (batch_id, feature_name, feature_value)
);

CREATE INDEX idx_batch_feature ON batch_psi_monitor (batch_id, feature_name);

3.3 表结构关系与数据流

下图展示了批次偏移模拟表在PSI监控体系中的位置与数据流转关系:

批次偏移模拟表数据流与PSI计算关系 基准批次数据 expected_count 当前批次数据 actual_count batch_psi_monitor batch_id | feature_name feature_value | actual | expected PSI计算结果 每个特征/批次 偏移告警 PSI > 阈值 核心数据流说明 1. 基准批次数据(如模型上线时的训练集)计算每个分箱的预期占比 → 写入 expected_count 2. 当前批次数据(如每日评分结果)计算每个分箱的实际占比 → 写入 actual_count 3. SQL聚合计算 PSI = Σ( (actual - expected) * ln(actual / expected) ),输出监控报表

3.4 插入模拟数据

为验证后续SQL脚本的正确性,需要插入一组具有代表性的模拟数据。以下示例包含两个特征(age 和 income_level)、两个批次(基准批次 B_202501 和当前批次 B_202502),每个特征分3个箱。

-- 插入模拟数据(MySQL / PostgreSQL 通用)
INSERT INTO batch_psi_monitor (batch_id, feature_name, feature_value, actual_count, expected_count) VALUES
-- 特征 age,基准批次
('B_202501', 'age', '[0-30)',  3000, 3000),
('B_202501', 'age', '[30-50)', 5000, 5000),
('B_202501', 'age', '[50+)',   2000, 2000),
-- 特征 age,当前批次(发生偏移:年轻群体增加,年长群体减少)
('B_202502', 'age', '[0-30)',  3800, 3000),
('B_202502', 'age', '[30-50)', 4900, 5000),
('B_202502', 'age', '[50+)',   1300, 2000),
-- 特征 income_level,基准批次
('B_202501', 'income_level', 'low',    4000, 4000),
('B_202501', 'income_level', 'medium', 4500, 4500),
('B_202501', 'income_level', 'high',   1500, 1500),
-- 特征 income_level,当前批次(轻微偏移)
('B_202502', 'income_level', 'low',    4200, 4000),
('B_202502', 'income_level', 'medium', 4400, 4500),
('B_202502', 'income_level', 'high',   1400, 1500);
数据设计意图:特征 age 的当前批次中,[0-30) 分箱的 actual_count(3800)明显高于 expected_count(3000),而 [50+) 分箱的 actual_count(1300)明显低于 expected_count(2000),这将导致较高的PSI值,便于后续章节验证偏移检测逻辑。特征 income_level 的偏移幅度较小,用于对比展示正常波动场景。

3.5 验证环境准备

执行以下查询,确认表结构正确且数据已成功插入:

-- 验证表结构
DESC batch_psi_monitor;  -- MySQL
\d+ batch_psi_monitor    -- PostgreSQL

-- 验证数据总量(预期12行)
SELECT COUNT(*) AS total_records FROM batch_psi_monitor;

-- 查看按批次和特征的汇总
SELECT batch_id, feature_name, 
       COUNT(*) AS num_bins,
       SUM(actual_count) AS total_actual,
       SUM(expected_count) AS total_expected
FROM batch_psi_monitor
GROUP BY batch_id, feature_name
ORDER BY batch_id, feature_name;