baa49d1a94
mv_overview_monthly是普通表不是物化视图,需要DELETE+INSERT更新 之前未刷新导致/overview API返回旧数据(6083万 vs bill_fact 6441万) 刷新脚本加入mv_overview_monthly更新步骤
117 lines
5.1 KiB
Bash
Executable File
117 lines
5.1 KiB
Bash
Executable File
#!/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 "=========================================="
|