3. SQL环境搭建:数据库选型、金融数据集导入与窗口/聚合函数基础

在开始组合风险归因与决策优化之前,必须搭建一个稳定、高效的SQL分析环境。本章将指导你完成数据库选型、金融数据集导入,并夯实窗口函数与聚合函数的基础——这是后续所有风险计算与归因分析的基石。

3.1 数据库选型:PostgreSQL vs MySQL

金融风控场景对SQL引擎的要求集中在:窗口函数支持度、复杂聚合性能、CTE(公用表表达式)递归能力以及数值精度。以下是两大主流开源数据库的对比:

对比维度 PostgreSQL MySQL
窗口函数支持 完整支持(RANK、DENSE_RANK、LAG、LEAD、FIRST_VALUE、LAST_VALUE、NTILE等) 8.0+ 支持基础窗口函数,但部分高级功能(如FILTER子句)缺失
CTE递归 原生支持递归CTE,适合层级归因与树状结构计算 8.0+ 支持递归CTE,但性能优化不如PG
数值精度 支持NUMERIC/DECIMAL高精度类型,适合金融金额计算 DECIMAL精度足够,但浮点运算存在隐式转换风险
并行查询 支持并行顺序扫描、并行聚合、并行JOIN 并行能力较弱,大表聚合易成瓶颈
扩展性 支持自定义聚合函数、扩展插件(如pg_stat_statements) 扩展能力有限,依赖存储引擎
金融风控推荐 ⭐⭐⭐⭐⭐(强烈推荐) ⭐⭐⭐(适合轻量级场景)
选型建议:本课程所有实战案例均基于 PostgreSQL 15+ 构建。如果你使用MySQL 8.0+,大部分窗口函数语法兼容,但部分高级归因计算(如递归层级归因)需要改写为存储过程或应用层实现。

3.2 金融数据集导入

我们将使用一个模拟的投资组合风险数据集,包含以下核心表:

  • portfolio_holdings — 持仓明细(证券代码、持仓数量、成本价、市值)
  • security_master — 证券主表(行业分类、板块、风险因子暴露)
  • daily_returns — 日收益率序列(日期、证券代码、收益率)
  • risk_factors — 风险因子数据(因子名称、因子收益率、协方差矩阵)

以下为建表与导入示例(PostgreSQL语法):

-- 创建持仓表
CREATE TABLE portfolio_holdings (
    holding_id SERIAL PRIMARY KEY,
    security_code VARCHAR(20) NOT NULL,
    holding_date DATE NOT NULL,
    quantity NUMERIC(18,4),
    cost_price NUMERIC(18,4),
    market_value NUMERIC(18,4),
    portfolio_id VARCHAR(20) DEFAULT 'PF_MAIN'
);

-- 创建证券主表
CREATE TABLE security_master (
    security_code VARCHAR(20) PRIMARY KEY,
    security_name VARCHAR(100),
    industry VARCHAR(50),
    sector VARCHAR(50),
    beta NUMERIC(10,4),
    market_cap NUMERIC(18,2)
);

-- 创建日收益率表
CREATE TABLE daily_returns (
    return_id SERIAL PRIMARY KEY,
    security_code VARCHAR(20) NOT NULL,
    return_date DATE NOT NULL,
    daily_return NUMERIC(10,6),
    UNIQUE(security_code, return_date)
);

-- 导入CSV数据(示例路径)
COPY portfolio_holdings(security_code, holding_date, quantity, cost_price, market_value)
FROM '/data/portfolio_holdings.csv'
DELIMITER ',' CSV HEADER;

COPY security_master(security_code, security_name, industry, sector, beta, market_cap)
FROM '/data/security_master.csv'
DELIMITER ',' CSV HEADER;

COPY daily_returns(security_code, return_date, daily_return)
FROM '/data/daily_returns.csv'
DELIMITER ',' CSV HEADER;
数据导入注意事项:
  • 金融数据中的金额字段建议使用 NUMERIC(18,4) 或 NUMERIC(18,2),避免浮点精度丢失。
  • 日期字段统一使用 DATE 类型,不要使用字符串存储。
  • 导入前检查CSV文件编码(推荐UTF-8),避免中文乱码。
  • 为 security_code + return_date 建立唯一索引,防止重复数据。

3.3 聚合函数基础

聚合函数是风险归因的计算核心。以下为金融风控中最常用的聚合函数及其应用场景:

聚合函数 语法示例 风控应用场景
SUM SUM(market_value) 计算组合总市值、行业总暴露
AVG AVG(daily_return) 计算平均日收益率、平均风险因子暴露
COUNT COUNT(DISTINCT security_code) 统计持仓证券数量、行业数量
MAX / MIN MAX(daily_return) 识别最大/最小日收益率、极端风险事件
STDDEV STDDEV(daily_return) 计算波动率(风险度量核心指标)
PERCENTILE_CONT PERCENTILE_CONT(0.05) WITHIN GROUP (ORDER BY daily_return) 计算VaR(风险价值)的分位数

实战示例:计算每个行业的持仓市值占比与波动率

SELECT
    sm.industry,
    SUM(ph.market_value) AS total_market_value,
    ROUND(SUM(ph.market_value) / SUM(SUM(ph.market_value)) OVER () * 100, 2) AS weight_pct,
    ROUND(STDDEV(dr.daily_return) * SQRT(252), 4) AS annualized_volatility
FROM portfolio_holdings ph
JOIN security_master sm ON ph.security_code = sm.security_code
JOIN daily_returns dr ON ph.security_code = dr.security_code
WHERE dr.return_date BETWEEN '2024-01-01' AND '2024-12-31'
GROUP BY sm.industry
ORDER BY total_market_value DESC;

3.4 窗口函数基础

窗口函数是组合风险归因的核心工具。与聚合函数不同,窗口函数不减少行数,而是在每一行上计算一个基于窗口范围的值。以下为三大类窗口函数:

  • 排序窗口函数:ROW_NUMBER、RANK、DENSE_RANK、NTILE — 用于风险排序、分位数划分
  • 偏移窗口函数:LAG、LEAD — 用于计算收益率变化、风险暴露偏移
  • 聚合窗口函数:SUM() OVER、AVG() OVER、STDDEV() OVER — 用于滚动计算、累计归因

语法结构:

窗口函数 OVER (
    [PARTITION BY 分组列]
    [ORDER BY 排序列]
    [ROWS/RANGE BETWEEN 起始行 AND 结束行]
)

实战示例:计算每只证券相对于其行业的累计收益率排名与滚动波动率

SELECT
    dr.security_code,
    sm.industry,
    dr.return_date,
    dr.daily_return,
    -- 行业内的累计收益率排名(按日期排序)
    SUM(dr.daily_return) OVER (
        PARTITION BY dr.security_code
        ORDER BY dr.return_date
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) AS cumulative_return,
    -- 行业内的波动率排名(按波动率降序)
    RANK() OVER (
        PARTITION BY sm.industry
        ORDER BY STDDEV(dr.daily_return) OVER (
            PARTITION BY dr.security_code
            ORDER BY dr.return_date
            ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING
        ) DESC
    ) AS vol_rank_in_industry,
    -- 前一日收益率(用于计算日间变化)
    LAG(dr.daily_return, 1) OVER (
        PARTITION BY dr.security_code
        ORDER BY dr.return_date
    ) AS prev_day_return
FROM daily_returns dr
JOIN security_master sm ON dr.security_code = sm.security_code
WHERE dr.return_date BETWEEN '2024-01-01' AND '2024-12-31'
ORDER BY dr.security_code, dr.return_date;
窗口函数 vs 聚合函数核心区别:
  • 聚合函数 + GROUP BY → 输出行数 = 分组数(数据压缩)
  • 窗口函数 + OVER → 输出行数 = 原表行数(保留明细)
  • 在风险归因中,窗口函数可以同时保留个体风险和组合风险信息,这是后续边际风险贡献计算的基础。

3.5 环境搭建验证与SVG架构图

完成数据库选型、建表与数据导入后,建议执行以下验证脚本:

-- 验证1:检查数据完整性
SELECT '持仓记录数' AS check_item, COUNT(*) AS cnt FROM portfolio_holdings
UNION ALL
SELECT '证券主表记录数', COUNT(*) FROM security_master
UNION ALL
SELECT '日收益率记录数', COUNT(*) FROM daily_returns;

-- 验证2:检查日期范围
SELECT MIN(return_date) AS start_date, MAX(return_date) AS end_date FROM daily_returns;

-- 验证3:检查窗口函数是否可用
SELECT security_code, return_date, daily_return,
       SUM(daily_return) OVER (ORDER BY return_date ROWS BETWEEN 5 PRECEDING AND CURRENT ROW) AS rolling_5d_return
FROM daily_returns
WHERE security_code = 'SEC001'
LIMIT 10;

以下为本章的SQL环境搭建与数据流架构图,展示从原始数据到风险归因计算的全链路:

SQL环境搭建与风险归因数据流架构 数据源层 CSV文件 / 交易系统API 持仓、证券、收益率、因子 数据库层 PostgreSQL 15+ COPY导入 / 索引 / 约束 SQL计算层 聚合函数 + 窗口函数 CTE递归 / 子查询 聚合函数核心功能 SUM / AVG / STDDEV PERCENTILE_CONT / COUNT 行业暴露 / 波动率 / VaR 窗口函数核心功能 RANK / LAG / LEAD SUM() OVER / AVG() OVER 滚动归因 / 边际贡献 风险归因输出 组合风险分解表 行业归因报告 因子暴露分析 环境搭建验证要点 ✅ 数据库选型确认:PostgreSQL 15+ 或 MySQL 8.0+ ✅ 金融数据集导入:持仓表、证券主表、日收益率表、因子表 ✅ 聚合函数验证:SUM/AVG/STDDEV/PERCENTILE_CONT 可用 ✅ 窗口函数验证:RANK/LAG/SUM() OVER 可用,支持ROWS/RANGE窗口

3.6 常见问题与避坑指南

问题场景 错误示例 正确做法
窗口函数与GROUP BY混用 SELECT industry, SUM(market_value), RANK() OVER(ORDER BY SUM(market_value)) FROM t GROUP BY industry 先聚合再窗口:使用子查询或CTE将聚合结果作为窗口函数的输入
窗口函数ORDER BY方向错误 SUM(return) OVER(ORDER BY date DESC) 导致累计逻辑混乱 明确时间顺序:ORDER BY date ASC 配合 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
金融金额使用FLOAT类型 market_value FLOAT 导致精度丢失 使用 NUMERIC(18,4) 或 DECIMAL(18,4)
忽略NULL值处理 AVG(daily_return) 包含NULL导致结果偏差 使用 COALESCE(daily_return, 0) 或 AVG(daily_return) FILTER(WHERE daily_return IS NOT NULL)
本章小结:你已经完成了SQL风险分析环境的搭建,掌握了聚合函数与窗口函数的核心语法。在下一阶段(第4章),我们将基于这些基础,构建组合风险因子暴露矩阵,并开始进行多因子归因计算。请确保当前环境中的窗口函数可以正常运行上述示例代码。