7ea6a2515f
- 归档根目录散落文档到 docs/20260812/ 和 docs/20260812-1/ - 新增 16-战略外脑实施方法论.md:七项能力、十二步工作法、AI与决策治理、成熟度和验收 - 新增 17-SBrainCO战略外脑建设对照表.md:方法论映射为领域模型、产品模块、API和分阶段待办 - 新增 调查表/战略外脑能力验收Checklist.csv:35项能力验收检查项 - 更新 01-总体框架与八步工作法.md:新增战略外脑扩展章节 - 更新 README.md:新增战略外脑体系导航 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
303 lines
11 KiB
Markdown
303 lines
11 KiB
Markdown
# 物化视图更新手册
|
|
|
|
## 一、物化视图依赖链
|
|
|
|
数据更新后必须按依赖顺序刷新物化视图,否则下游数据不一致。
|
|
|
|
```
|
|
bill_records (原始表)
|
|
│
|
|
▼ REFRESH
|
|
bill_fact (物化视图) ★ 第一层,最关键
|
|
│
|
|
├─▶ mv_daily_revenue
|
|
├─▶ mv_store_risk_rating_monthly ★ 门店评估基础
|
|
├─▶ mv_dish_sku_abc_monthly
|
|
├─▶ mv_dish_sku_summary_monthly
|
|
├─▶ mv_dish_store_summary_monthly
|
|
├─▶ mv_dish_basket_monthly
|
|
├─▶ mv_dish_pair_summary_monthly
|
|
├─▶ mv_store_action_priority_deep_monthly
|
|
├─▶ mv_store_area_efficiency_monthly
|
|
├─▶ mv_store_benchmark_composite_monthly
|
|
├─▶ mv_store_category_mix_monthly
|
|
├─▶ mv_store_deep_diagnosis_monthly
|
|
├─▶ mv_store_member_opportunity_monthly
|
|
├─▶ mv_store_overlap_risk_monthly
|
|
├─▶ mv_store_platform_economics_monthly
|
|
├─▶ mv_store_site_profile_monthly
|
|
├─▶ mv_store_site_replication_monthly
|
|
├─▶ mv_store_repeat_summary_monthly
|
|
├─▶ mv_district_site_benchmark_monthly
|
|
├─▶ mv_site_segment_benchmark_monthly
|
|
└─▶ mv_store_theoretical_actual_cost_monthly ★ 门店成本
|
|
|
|
inventory_cost_records (原始表)
|
|
│
|
|
▼ REFRESH
|
|
mv_inventory_cost_classified_monthly
|
|
|
|
operating_expense_records (原始表)
|
|
│
|
|
▼ VIEW (无需刷新)
|
|
v_operating_expense_unit_monthly
|
|
│
|
|
▼ VIEW (无需刷新)
|
|
v_store_operating_expense_monthly (按 sales_store_code 聚合)
|
|
│
|
|
▼ INSERT/UPDATE (需手动操作)
|
|
mv_store_operating_expense_monthly (物化表)
|
|
```
|
|
|
|
---
|
|
|
|
## 二、刷新命令清单
|
|
|
|
### 2.1 账单数据更新后(必刷)
|
|
|
|
```sql
|
|
-- ★ 第一优先级:bill_fact 是所有分析的基础
|
|
REFRESH MATERIALIZED VIEW analytics.bill_fact;
|
|
|
|
-- ★ 第二优先级:门店评估相关
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_risk_rating_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_theoretical_actual_cost_monthly;
|
|
|
|
-- 第三优先级:菜品分析
|
|
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_abc_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_summary_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_dish_store_summary_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_dish_basket_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_dish_pair_summary_monthly;
|
|
|
|
-- 第四优先级:门店深度分析
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_action_priority_deep_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_area_efficiency_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_benchmark_composite_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_category_mix_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_deep_diagnosis_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_member_opportunity_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_overlap_risk_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_platform_economics_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_site_profile_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_site_replication_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_repeat_summary_monthly;
|
|
|
|
-- 第五优先级:日度与区域
|
|
REFRESH MATERIALIZED VIEW analytics.mv_daily_revenue;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_district_site_benchmark_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_site_segment_benchmark_monthly;
|
|
```
|
|
|
|
### 2.2 库存成本数据更新后
|
|
|
|
```sql
|
|
REFRESH MATERIALIZED VIEW analytics.mv_inventory_cost_classified_monthly;
|
|
-- 理论vs实际成本也依赖库存数据
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_theoretical_actual_cost_monthly;
|
|
```
|
|
|
|
### 2.3 费用数据更新后
|
|
|
|
```sql
|
|
-- v_operating_expense_unit_monthly 和 v_store_operating_expense_monthly 是普通视图,无需刷新
|
|
-- 但 mv_store_operating_expense_monthly 是物化表,需要手动更新:
|
|
|
|
-- 方法1:删除+插入(推荐,简单可靠)
|
|
DELETE FROM analytics.mv_store_operating_expense_monthly WHERE report_month = '2026-04-01';
|
|
INSERT INTO analytics.mv_store_operating_expense_monthly
|
|
SELECT * FROM analytics.v_store_operating_expense_monthly WHERE report_month = '2026-04-01';
|
|
|
|
-- 方法2:全量重建
|
|
TRUNCATE analytics.mv_store_operating_expense_monthly;
|
|
INSERT INTO analytics.mv_store_operating_expense_monthly
|
|
SELECT * FROM analytics.v_store_operating_expense_monthly;
|
|
```
|
|
|
|
---
|
|
|
|
## 三、一键刷新脚本
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# refresh_all_mv.sh - 刷新所有物化视图
|
|
# 用法: bash refresh_all_mv.sh [month] (默认 2026-04)
|
|
|
|
MONTH=${1:-2026-04}
|
|
MONTH_START="${MONTH}-01"
|
|
|
|
psql -h localhost -p 5432 -U freedak -d bill_query <<EOF
|
|
-- 第一层
|
|
REFRESH MATERIALIZED VIEW analytics.bill_fact;
|
|
|
|
-- 第二层
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_risk_rating_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_theoretical_actual_cost_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_daily_revenue;
|
|
|
|
-- 第三层
|
|
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_abc_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_summary_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_dish_store_summary_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_dish_basket_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_dish_pair_summary_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_action_priority_deep_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_area_efficiency_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_benchmark_composite_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_category_mix_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_deep_diagnosis_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_member_opportunity_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_overlap_risk_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_platform_economics_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_site_profile_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_site_replication_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_store_repeat_summary_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_district_site_benchmark_monthly;
|
|
REFRESH MATERIALIZED VIEW analytics.mv_site_segment_benchmark_monthly;
|
|
|
|
-- 库存成本
|
|
REFRESH MATERIALIZED VIEW analytics.mv_inventory_cost_classified_monthly;
|
|
|
|
-- 费用物化表
|
|
DELETE FROM analytics.mv_store_operating_expense_monthly WHERE report_month = '${MONTH_START}';
|
|
INSERT INTO analytics.mv_store_operating_expense_monthly
|
|
SELECT * FROM analytics.v_store_operating_expense_monthly WHERE report_month = '${MONTH_START}';
|
|
|
|
-- 验证
|
|
SELECT 'bill_fact' AS source, count(DISTINCT store_code) AS stores, round(sum(received_total)::numeric,2) AS received
|
|
FROM analytics.bill_fact WHERE closed_at >= '${MONTH_START}' AND closed_at < ('${MONTH_START}'::date + interval '1 month')
|
|
UNION ALL
|
|
SELECT 'risk_rating', count(*), round(sum(received)::numeric,2)
|
|
FROM analytics.mv_store_risk_rating_monthly WHERE month_start = '${MONTH_START}'
|
|
UNION ALL
|
|
SELECT 'theo_cost', count(*), round(sum(sales_received)::numeric,2)
|
|
FROM analytics.mv_store_theoretical_actual_cost_monthly WHERE month_start = '${MONTH_START}'
|
|
UNION ALL
|
|
SELECT 'op_expense', count(*), round(sum(received)::numeric,2)
|
|
FROM analytics.mv_store_operating_expense_monthly WHERE report_month = '${MONTH_START}';
|
|
EOF
|
|
```
|
|
|
|
---
|
|
|
|
## 四、物化视图 vs 普通视图 vs 物化表
|
|
|
|
| 类型 | 刷新方式 | 示例 | 说明 |
|
|
|------|----------|------|------|
|
|
| 物化视图 (Materialized View) | `REFRESH MATERIALIZED VIEW` | `bill_fact`, `mv_store_risk_rating_monthly` | 数据快照,需手动刷新 |
|
|
| 普通视图 (View) | 实时查询,无需刷新 | `v_store_operating_expense_monthly`, `v_operating_expense_account_monthly` | 每次查询实时计算 |
|
|
| 物化表 (Table) | `INSERT/UPDATE/DELETE` | `mv_store_operating_expense_monthly` | 物理表,需手动写入 |
|
|
|
|
### 判断方法
|
|
|
|
```sql
|
|
-- 查看对象类型
|
|
SELECT relname, relkind
|
|
FROM pg_class
|
|
WHERE relname LIKE 'mv_%' OR relname LIKE 'v_%'
|
|
AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'analytics');
|
|
|
|
-- relkind 含义:
|
|
-- 'm' = 物化视图 (需要 REFRESH)
|
|
-- 'v' = 普通视图 (无需刷新)
|
|
-- 'r' = 普通表 (需要 INSERT/UPDATE)
|
|
```
|
|
|
|
---
|
|
|
|
## 五、常见问题
|
|
|
|
### 5.1 数据不一致
|
|
|
|
**症状**: API返回的门店数少于实际门店数,或实收合计与 bill_fact 不一致
|
|
|
|
**原因**: 导入新数据后未刷新物化视图
|
|
|
|
**修复**: 执行上述刷新命令
|
|
|
|
### 5.2 费用未纳入门店评估
|
|
|
|
**症状**: 某门店在评估列表中缺失或费用为0
|
|
|
|
**原因**:
|
|
1. `operating_expense_store_mapping` 中 `sales_store_code` 未设置
|
|
2. `include_in_operating_analysis` 标志为 false
|
|
3. `mv_store_operating_expense_monthly` 物化表未更新
|
|
|
|
**修复**:
|
|
```sql
|
|
-- 检查映射
|
|
SELECT * FROM operating_expense_store_mapping WHERE cost_unit_source_name ILIKE '%门店名%';
|
|
|
|
-- 更新映射
|
|
UPDATE operating_expense_store_mapping
|
|
SET sales_store_code = '0022', include_in_operating_analysis = true
|
|
WHERE cost_unit_source_name = '哈马尔罕大钟寺店';
|
|
|
|
-- 刷新物化表
|
|
DELETE FROM analytics.mv_store_operating_expense_monthly WHERE report_month = '2026-04-01';
|
|
INSERT INTO analytics.mv_store_operating_expense_monthly
|
|
SELECT * FROM analytics.v_store_operating_expense_monthly WHERE report_month = '2026-04-01';
|
|
```
|
|
|
|
### 5.3 费用重复行
|
|
|
|
**症状**: 同一门店在 `mv_store_operating_expense_monthly` 中出现多行
|
|
|
|
**原因**: 多个费用单位映射到同一 `sales_store_code`,但视图未按 `sales_store_code` 聚合
|
|
|
|
**修复**: 确认 `v_store_operating_expense_monthly` 视图定义包含 `expense_agg` CTE,按 `sales_store_code` GROUP BY
|
|
|
|
### 5.4 固定月份物化视图
|
|
|
|
**问题**: `*_april` 系列物化视图硬编码2026年4月,不支持其他月份
|
|
|
|
**影响**: 5月数据需要创建新的 `*_may` 系列物化视图,或修改为通用月份
|
|
|
|
**当前状态**: `v_store_operating_expense_monthly` 视图中 sales/food/loc CTE 引用了 `v_store_site_profile_april` 和 `v_store_theoretical_actual_cost_april`,这些是4月专用视图
|
|
|
|
---
|
|
|
|
## 六、数据一致性验证
|
|
|
|
刷新后执行以下验证:
|
|
|
|
```sql
|
|
-- 1. 门店数一致性
|
|
SELECT 'bill_fact' AS src, count(DISTINCT store_code) AS stores
|
|
FROM analytics.bill_fact
|
|
WHERE closed_at >= '2026-04-01' AND closed_at < '2026-05-01'
|
|
UNION ALL
|
|
SELECT 'risk_rating', count(*)
|
|
FROM analytics.mv_store_risk_rating_monthly WHERE month_start = '2026-04-01'
|
|
UNION ALL
|
|
SELECT 'theo_cost', count(*)
|
|
FROM analytics.mv_store_theoretical_actual_cost_monthly WHERE month_start = '2026-04-01'
|
|
UNION ALL
|
|
SELECT 'op_expense', count(*)
|
|
FROM analytics.mv_store_operating_expense_monthly WHERE report_month = '2026-04-01';
|
|
|
|
-- 2. 实收一致性
|
|
SELECT 'bill_fact' AS src, round(sum(received_total)::numeric,2) AS received
|
|
FROM analytics.bill_fact
|
|
WHERE closed_at >= '2026-04-01' AND closed_at < '2026-05-01'
|
|
UNION ALL
|
|
SELECT 'risk_rating', round(sum(received)::numeric,2)
|
|
FROM analytics.mv_store_risk_rating_monthly WHERE month_start = '2026-04-01'
|
|
UNION ALL
|
|
SELECT 'theo_cost', round(sum(sales_received)::numeric,2)
|
|
FROM analytics.mv_store_theoretical_actual_cost_monthly WHERE month_start = '2026-04-01'
|
|
UNION ALL
|
|
SELECT 'op_expense', round(sum(received)::numeric,2)
|
|
FROM analytics.mv_store_operating_expense_monthly WHERE report_month = '2026-04-01';
|
|
|
|
-- 3. 差异门店排查
|
|
SELECT store_code, store_name, received
|
|
FROM analytics.mv_store_risk_rating_monthly
|
|
WHERE month_start = '2026-04-01' AND received > 0
|
|
AND store_code NOT IN (
|
|
SELECT DISTINCT store_code FROM analytics.mv_store_operating_expense_monthly
|
|
WHERE report_month = '2026-04-01'
|
|
);
|
|
```
|