Files
SBrainCO/db/migration_phase11_materialize_all.sql
T
freedakgmail 122290e3b0 perf: Phase 12 — 物化剩余9个慢函数,全量API优化完成
新增物化视图:
- mv_store_action_priority_deep_monthly (10.7s→3ms)
- mv_inventory_cost_classified_monthly (37ms→3ms, 消除重复调用)
- mv_dish_pair_summary_monthly (171s→3ms)
- mv_store_area_efficiency_monthly (10.1s→3ms)
- mv_store_site_profile_monthly (9.3s→3ms)
- mv_site_segment_benchmark_monthly (12s→3ms)
- mv_store_site_replication_monthly (12.6s→3ms)
- mv_store_overlap_risk_monthly (88s→3ms)
- mv_district_site_benchmark_monthly (56s→3ms)

后端: 所有 fn_ 函数调用替换为物化视图查询
前端: BossPage 添加 MonthPicker 组件并传递 month 参数
修复: cost/category-benchmark 排序列名 store_code→sales_store_code
总计: 21个物化视图, 全部API响应<1.7秒
2026-08-01 14:51:24 +08:00

178 lines
9.6 KiB
PL/PgSQL

-- ============================================================
-- Phase 11: 全量物化视图 — 按月预计算所有慢函数
-- 数据导入后调用 analytics.refresh_all_monthly_views() 刷新
-- ============================================================
-- 1. mv_store_risk_rating_monthly
DROP MATERIALIZED VIEW IF EXISTS analytics.mv_store_risk_rating_monthly;
CREATE MATERIALIZED VIEW analytics.mv_store_risk_rating_monthly AS
SELECT m.month_start::date AS month_start, r.*
FROM generate_series(
(SELECT date_trunc('month', min(closed_at))::date FROM analytics.bill_fact),
(SELECT date_trunc('month', max(closed_at))::date FROM analytics.bill_fact),
interval '1 month'
) AS m(month_start)
CROSS JOIN LATERAL analytics.fn_store_risk_rating(m.month_start::date) r;
CREATE UNIQUE INDEX idx_mv_risk_rating_month_store ON analytics.mv_store_risk_rating_monthly (month_start, store_code);
CREATE INDEX idx_mv_risk_rating_month ON analytics.mv_store_risk_rating_monthly (month_start);
-- ============================================================
-- 2. mv_store_platform_economics_monthly
-- ============================================================
DROP MATERIALIZED VIEW IF EXISTS analytics.mv_store_platform_economics_monthly;
CREATE MATERIALIZED VIEW analytics.mv_store_platform_economics_monthly AS
SELECT m.month_start::date AS month_start, r.*
FROM generate_series(
(SELECT date_trunc('month', min(closed_at))::date FROM analytics.bill_fact),
(SELECT date_trunc('month', max(closed_at))::date FROM analytics.bill_fact),
interval '1 month'
) AS m(month_start)
CROSS JOIN LATERAL analytics.fn_store_platform_economics(m.month_start::date) r;
CREATE UNIQUE INDEX idx_mv_platform_econ_month_store ON analytics.mv_store_platform_economics_monthly (month_start, store_code);
CREATE INDEX idx_mv_platform_econ_month ON analytics.mv_store_platform_economics_monthly (month_start);
-- ============================================================
-- 3. mv_store_category_mix_monthly
-- ============================================================
DROP MATERIALIZED VIEW IF EXISTS analytics.mv_store_category_mix_monthly;
CREATE MATERIALIZED VIEW analytics.mv_store_category_mix_monthly AS
SELECT m.month_start::date AS month_start, r.*
FROM generate_series(
(SELECT date_trunc('month', min(closed_at))::date FROM analytics.bill_fact),
(SELECT date_trunc('month', max(closed_at))::date FROM analytics.bill_fact),
interval '1 month'
) AS m(month_start)
CROSS JOIN LATERAL analytics.fn_store_category_mix(m.month_start::date) r;
CREATE UNIQUE INDEX idx_mv_cat_mix_month_store ON analytics.mv_store_category_mix_monthly (month_start, store_code);
CREATE INDEX idx_mv_cat_mix_month ON analytics.mv_store_category_mix_monthly (month_start);
-- ============================================================
-- 4. mv_store_member_opportunity_monthly
-- ============================================================
DROP MATERIALIZED VIEW IF EXISTS analytics.mv_store_member_opportunity_monthly;
CREATE MATERIALIZED VIEW analytics.mv_store_member_opportunity_monthly AS
SELECT m.month_start::date AS month_start, r.*
FROM generate_series(
(SELECT date_trunc('month', min(closed_at))::date FROM analytics.bill_fact),
(SELECT date_trunc('month', max(closed_at))::date FROM analytics.bill_fact),
interval '1 month'
) AS m(month_start)
CROSS JOIN LATERAL analytics.fn_store_member_opportunity(m.month_start::date) r;
CREATE UNIQUE INDEX idx_mv_member_opp_month_store ON analytics.mv_store_member_opportunity_monthly (month_start, store_code);
CREATE INDEX idx_mv_member_opp_month ON analytics.mv_store_member_opportunity_monthly (month_start);
-- ============================================================
-- 5. mv_store_benchmark_composite_monthly
-- ============================================================
DROP MATERIALIZED VIEW IF EXISTS analytics.mv_store_benchmark_composite_monthly;
CREATE MATERIALIZED VIEW analytics.mv_store_benchmark_composite_monthly AS
SELECT m.month_start::date AS month_start, r.*
FROM generate_series(
(SELECT date_trunc('month', min(closed_at))::date FROM analytics.bill_fact),
(SELECT date_trunc('month', max(closed_at))::date FROM analytics.bill_fact),
interval '1 month'
) AS m(month_start)
CROSS JOIN LATERAL analytics.fn_store_benchmark_composite(m.month_start::date) r;
CREATE UNIQUE INDEX idx_mv_benchmark_month_store ON analytics.mv_store_benchmark_composite_monthly (month_start, store_code);
CREATE INDEX idx_mv_benchmark_month ON analytics.mv_store_benchmark_composite_monthly (month_start);
-- ============================================================
-- 6. mv_store_deep_diagnosis_monthly
-- ============================================================
DROP MATERIALIZED VIEW IF EXISTS analytics.mv_store_deep_diagnosis_monthly;
CREATE MATERIALIZED VIEW analytics.mv_store_deep_diagnosis_monthly AS
SELECT m.month_start::date AS month_start, r.*
FROM generate_series(
(SELECT date_trunc('month', min(closed_at))::date FROM analytics.bill_fact),
(SELECT date_trunc('month', max(closed_at))::date FROM analytics.bill_fact),
interval '1 month'
) AS m(month_start)
CROSS JOIN LATERAL analytics.fn_store_deep_diagnosis(m.month_start::date) r;
CREATE UNIQUE INDEX idx_mv_deep_diag_month_store ON analytics.mv_store_deep_diagnosis_monthly (month_start, store_code);
CREATE INDEX idx_mv_deep_diag_month ON analytics.mv_store_deep_diagnosis_monthly (month_start);
-- ============================================================
-- 7. mv_dish_basket_monthly
-- ============================================================
DROP MATERIALIZED VIEW IF EXISTS analytics.mv_dish_basket_monthly;
CREATE MATERIALIZED VIEW analytics.mv_dish_basket_monthly AS
SELECT m.month_start::date AS month_start, r.*
FROM generate_series(
(SELECT date_trunc('month', min(closed_at))::date FROM analytics.bill_fact),
(SELECT date_trunc('month', max(closed_at))::date FROM analytics.bill_fact),
interval '1 month'
) AS m(month_start)
CROSS JOIN LATERAL analytics.fn_dish_basket(m.month_start::date) r;
CREATE UNIQUE INDEX idx_mv_dish_basket_month ON analytics.mv_dish_basket_monthly (month_start, store_code, bill_no);
CREATE INDEX idx_mv_dish_basket_month_start ON analytics.mv_dish_basket_monthly (month_start);
-- ============================================================
-- 8. mv_dish_store_summary_monthly
-- ============================================================
DROP MATERIALIZED VIEW IF EXISTS analytics.mv_dish_store_summary_monthly;
CREATE MATERIALIZED VIEW analytics.mv_dish_store_summary_monthly AS
SELECT m.month_start::date AS month_start, r.*
FROM generate_series(
(SELECT date_trunc('month', min(closed_at))::date FROM analytics.bill_fact),
(SELECT date_trunc('month', max(closed_at))::date FROM analytics.bill_fact),
interval '1 month'
) AS m(month_start)
CROSS JOIN LATERAL analytics.fn_dish_store_summary(m.month_start::date) r;
CREATE UNIQUE INDEX idx_mv_dish_store_summary_month ON analytics.mv_dish_store_summary_monthly (month_start, store_code);
CREATE INDEX idx_mv_dish_store_summary_month_start ON analytics.mv_dish_store_summary_monthly (month_start);
-- ============================================================
-- 9. mv_store_theoretical_actual_cost_monthly
-- ============================================================
DROP MATERIALIZED VIEW IF EXISTS analytics.mv_store_theoretical_actual_cost_monthly;
CREATE MATERIALIZED VIEW analytics.mv_store_theoretical_actual_cost_monthly AS
SELECT m.month_start::date AS month_start, r.*
FROM generate_series(
(SELECT date_trunc('month', min(closed_at))::date FROM analytics.bill_fact),
(SELECT date_trunc('month', max(closed_at))::date FROM analytics.bill_fact),
interval '1 month'
) AS m(month_start)
CROSS JOIN LATERAL analytics.fn_store_theoretical_actual_cost(m.month_start::date) r;
CREATE UNIQUE INDEX idx_mv_theor_actual_month_store ON analytics.mv_store_theoretical_actual_cost_monthly (month_start, store_code);
CREATE INDEX idx_mv_theor_actual_month ON analytics.mv_store_theoretical_actual_cost_monthly (month_start);
-- ============================================================
-- 刷新函数 — 数据导入后调用
-- ============================================================
CREATE OR REPLACE FUNCTION analytics.refresh_all_monthly_views()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_summary_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_abc_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_risk_rating_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_platform_economics_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_category_mix_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_member_opportunity_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_benchmark_composite_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_deep_diagnosis_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_basket_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_store_summary_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_theoretical_actual_cost_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_action_priority_deep_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_inventory_cost_classified_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_pair_summary_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_area_efficiency_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_site_profile_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_site_segment_benchmark_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_site_replication_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_overlap_risk_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_district_site_benchmark_monthly;
END;
$function$;