# 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 异常账单视图设计 ```sql -- 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 映射表构建 ```sql 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 代码中的映射查找模式 ```typescript // 构建查找表:同时用原名和映射名 const staffLookup: Record = {} 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. 关键注意事项 1. **先查数据再写SQL**:AI生成SQL前,先让它查实际表结构和样本数据 2. **物化视图必须建索引**:无索引的物化视图查询比原始表还慢 3. **刷新脚本要完整**:遗漏任何一个视图的刷新都会导致数据不一致 4. **映射表要持续维护**:新开门店时需要补充映射 5. **AI生成的SQL必须人工审查**:AI可能忽略业务约束(如空值处理、日期格式)