Files
SBrainCO/scripts/refresh_materialized_views.sh
T
freedakgmail baa49d1a94 fix: 刷新mv_overview_monthly,修复实收口径不一致
mv_overview_monthly是普通表不是物化视图,需要DELETE+INSERT更新
之前未刷新导致/overview API返回旧数据(6083万 vs bill_fact 6441万)
刷新脚本加入mv_overview_monthly更新步骤
2026-08-11 20:02:41 +08:00

117 lines
5.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# refresh_materialized_views.sh
# 数据导入后刷新所有物化视图并更新费用物化表
# 用法: bash scripts/refresh_materialized_views.sh [YYYY-MM-DD]
# 示例: bash scripts/refresh_materialized_views.sh 2026-04-01
set -e
MONTH_START="${1:-$(date -u +%Y-%m-01)}"
DB_HOST="${DB_HOST:-localhost}"
DB_PORT="${DB_PORT:-5432}"
DB_USER="${DB_USER:-freedak}"
DB_NAME="${DB_NAME:-bill_query}"
echo "=========================================="
echo " 刷新物化视图"
echo " 月份: ${MONTH_START}"
echo " 数据库: ${DB_NAME}@${DB_HOST}:${DB_PORT}"
echo "=========================================="
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" <<EOF
-- 第一层:账单事实
REFRESH MATERIALIZED VIEW analytics.bill_fact;
SELECT 'bill_fact refreshed' AS status;
-- 第一层b:总览汇总表(普通表,需DELETE+INSERT
DELETE FROM analytics.mv_overview_monthly WHERE month = '${MONTH_START}';
INSERT INTO analytics.mv_overview_monthly
SELECT
date_trunc('month', closed_at)::date AS month,
count(*) AS bill_count,
round(sum(received_total)::numeric, 2) AS received,
round(avg(received_total)::numeric, 2) AS avg_bill_value,
round(avg(CASE WHEN consumption > 0 THEN discount_total::numeric / consumption * 100 ELSE 0 END)::numeric, 2) AS discount_rate_pct,
0 AS theoretical_margin_pct,
0 AS member_bills,
0 AS member_share_pct
FROM analytics.bill_fact
WHERE closed_at >= '${MONTH_START}' AND closed_at < ('${MONTH_START}'::date + interval '1 month')
GROUP BY date_trunc('month', closed_at);
SELECT 'mv_overview_monthly updated' AS status;
-- 第二层:门店评估
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;
SELECT 'core MVs refreshed' AS status;
-- 第三层:菜品分析
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;
SELECT 'dish MVs refreshed' AS status;
-- 第四层:门店深度分析
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;
SELECT 'store deep MVs refreshed' AS status;
-- 第五层:区域与选址
REFRESH MATERIALIZED VIEW analytics.mv_district_site_benchmark_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_site_segment_benchmark_monthly;
SELECT 'region MVs refreshed' AS status;
-- 库存成本
REFRESH MATERIALIZED VIEW analytics.mv_inventory_cost_classified_monthly;
SELECT 'inventory MV refreshed' AS status;
-- 费用物化表(从视图写入)
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 'expense table updated' AS status;
-- 验证
SELECT '=== 数据一致性验证 ===' AS header;
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}';
SELECT '=== 门店覆盖检查 ===' AS header;
SELECT r.store_code, r.store_name, r.received,
CASE WHEN e.sales_store_code IS NOT NULL THEN '有费用' ELSE '无费用' END AS expense_status
FROM analytics.mv_store_risk_rating_monthly r
LEFT JOIN analytics.mv_store_operating_expense_monthly e
ON r.store_code = e.sales_store_code AND e.report_month = '${MONTH_START}'
WHERE r.month_start = '${MONTH_START}' AND r.received > 0
AND e.sales_store_code IS NULL
ORDER BY r.received DESC;
EOF
echo "=========================================="
echo " 刷新完成!"
echo "=========================================="