6.5 KiB
6.5 KiB
06 · 步骤五:AI协同本体层构建
目标:AI参与数据层代码编写——物化视图SQL、导入脚本、数据质量校验,并逐项验证。
[Delta主导,AI为主]FDE角色:这一步是Delta层的核心工作。AI承担80%的代码生成(SQL、脚本),人聚焦审查和验证。本体层的沉淀质量直接决定下一个同类客户的交付速度。
1. AI协同模式
1.1 工作流程
1. 人工提供:数据现状(表结构、样本数据、数据量)
2. AI生成:物化视图SQL草案
3. 人工审查:业务逻辑是否正确
4. AI执行:在数据库上创建视图 + 验证数据
5. 人工确认:抽查数据准确性
6. 迭代:发现问题→AI修复→再验证
1.2 AI Prompt模式
"请基于以下原始表设计物化视图:
- 原始表:bill_records(字段:c003门店, c009消费, c068优惠, c114实收, c175下单时间, c176结账时间, c191收银员)
- 需求:按月×门店×小时聚合账单数、消费、优惠、实收
- 注意:c114可能为空字符串,需COALESCE处理
输出:CREATE MATERIALIZED VIEW SQL + 索引SQL"
2. 物化视图构建
2.1 构建顺序
1. 基础视图(不依赖其他视图)
├── mv_bill_hourly(账单小时聚合)
├── mv_overview_daily(日度概览)
└── mv_overview_monthly(月度概览)
2. 门店评级视图(依赖基础视图)
├── mv_store_risk_rating_monthly(门店风险评级)
└── mv_store_benchmark_composite_monthly(门店对标)
3. 费用视图(依赖外部费用数据)
└── mv_store_operating_expense_monthly(门店费用)
4. 风险明细视图(依赖原始表)
├── mv_risk_anomaly(异常账单)
├── mv_risk_zero(零实收)
└── mv_risk_cashier(收银员风险)
5. 平台视图
└── mv_store_platform_economics_monthly(平台经济性)
2.2 AI协同编写物化视图
典型交互:
人:bill_records中c003是门店名,c114是实收金额,但有些是空字符串
AI:生成SQL:
CREATE MATERIALIZED VIEW mv_bill_hourly AS
SELECT
c003 AS store_name,
EXTRACT(HOUR FROM c176::timestamp) AS hour,
to_char(c176::timestamp, 'YYYY-MM') AS month,
count(*) AS bills,
sum(COALESCE(NULLIF(c009,'')::numeric, 0)) AS consumption,
sum(COALESCE(NULLIF(c114,'')::numeric, 0)) AS received
FROM bill_records
WHERE c176 IS NOT NULL
GROUP BY 1, 2, 3;
人:门店名需要和费用表对齐,有些名字不一样
AI:补充映射逻辑,使用store_name_mapping
2.3 异常账单视图设计
-- AI协同设计的异常判断逻辑
CASE
WHEN consumption > 0 AND received = 0 THEN '有消费无实收'
WHEN discount > consumption THEN '优惠大于消费'
WHEN abs(consumption - discount - received) > 1 THEN '消费-优惠与实收不平'
ELSE NULL
END
关键教训:阈值0.05元太严格,会将浮点舍入差异标为异常。AI建议初始阈值设为1元,后续根据数据分布调整。
3. 数据导入脚本
3.1 AI协同编写导入脚本
人:这是4月的薪资Excel,列名是中文,需要导入salary_detail_records
AI:生成Python/SQL导入脚本,包含:
- 列名映射(中文→英文字段名)
- 数据类型转换
- 空值处理
- 去重逻辑
- 进度输出
3.2 导入关键点
| 数据源 | 关键问题 | 解决方案 |
|---|---|---|
| bill_records | c001~c200列名无含义 | 建立字段映射文档 |
| salary_detail_records | salary_period格式"2026年4月" | 查询时用to_char中文格式 |
| attendance_records | department是路径字符串 | extractStore从后往前找"店" |
| 费用数据 | 门店名与账单系统不一致 | store_name_mapping映射表 |
4. 数据质量校验
4.1 AI协同校验
AI Prompt:
"请对以下物化视图进行数据质量校验:
1. 记录数是否合理(与原始表对比)
2. 金额加总是否一致(物化视图 vs 原始表)
3. 是否有NULL或异常值
4. 门店数是否完整
输出:校验SQL + 预期结果 + 实际结果"
4.2 校验清单
| 校验项 | SQL | 预期 |
|---|---|---|
| 门店数 | SELECT count(DISTINCT store_name) FROM mv_bill_hourly WHERE month='2026-04' |
~94家 |
| 实收总额 | SELECT sum(received) FROM mv_bill_hourly WHERE month='2026-04' |
~6369万 |
| 账单总数 | SELECT sum(bills) FROM mv_bill_hourly WHERE month='2026-04' |
~168万 |
| 异常账单占比 | SELECT count(*) FROM mv_risk_anomaly / SELECT count(*) FROM bill_records |
<5% |
| 物化视图新鲜度 | 对比物化视图和原始表的count | 一致 |
5. 门店名映射
5.1 映射表构建
CREATE TABLE public.store_name_mapping (
salary_name TEXT, -- 薪资/考勤系统中的名称
bill_name TEXT -- 账单系统中的名称
);
5.2 AI协同发现映射
AI Prompt:
"请对比以下两个数据源的门店名,找出不一致的:
- 账单系统:SELECT DISTINCT store_name FROM mv_bill_hourly
- 考勤系统:SELECT DISTINCT extractStore(department) FROM attendance_records
输出:需要映射的对照表"
5.3 代码中的映射查找模式
// 构建查找表:同时用原名和映射名
const staffLookup: Record<string, any> = {}
for (const [store, data] of Object.entries(staffSummary)) {
staffLookup[store] = data // 原名
const mapped = nameMap[store]
if (mapped && mapped !== store) {
staffLookup[mapped] = data // 映射名
}
}
6. 输出物
| 输出物 | 说明 | 验证方式 |
|---|---|---|
| 物化视图SQL | 所有视图的CREATE语句 | psql执行成功 |
| 索引SQL | 每个视图的索引 | 查询性能达标 |
| 导入脚本 | 各数据源的导入脚本 | 导入后数据量正确 |
| 映射表 | store_name_mapping | 所有门店能匹配 |
| 数据质量报告 | 校验结果汇总 | 人工抽查确认 |
| 刷新脚本 | 物化视图刷新流程 | 执行后数据更新 |
7. 关键注意事项
- 先查数据再写SQL:AI生成SQL前,先让它查实际表结构和样本数据
- 物化视图必须建索引:无索引的物化视图查询比原始表还慢
- 刷新脚本要完整:遗漏任何一个视图的刷新都会导致数据不一致
- 映射表要持续维护:新开门店时需要补充映射
- AI生成的SQL必须人工审查:AI可能忽略业务约束(如空值处理、日期格式)