-- ============================================================ -- 10_materialize_slow_views.sql -- 将门店详情页慢视图转为物化视图,加 store_code 索引 -- 预期:/stores/:code 从 ~15s 降至 <100ms -- ============================================================ -- ============================================================ -- Step 1: 依赖视图按逆序 DROP -- ============================================================ DROP VIEW IF EXISTS analytics.v_store_execution_priority; DROP VIEW IF EXISTS analytics.v_store_deep_diagnosis_april; DROP VIEW IF EXISTS analytics.v_store_benchmark_composite; DROP VIEW IF EXISTS analytics.v_store_action_list; -- ============================================================ -- Step 2: 基础视图 → 物化视图 -- ============================================================ -- 2a. v_store_platform_economics DROP VIEW IF EXISTS analytics.v_store_platform_economics; CREATE MATERIALIZED VIEW analytics.v_store_platform_economics AS SELECT NULLIF(bill_records.c002, ''::text) AS store_code, NULLIF(bill_records.c003, ''::text) AS store_name, sum(COALESCE(NULLIF(bill_records.c151, ''::text)::numeric, 0::numeric)) AS meituan_received, sum(COALESCE(NULLIF(bill_records.c101, ''::text)::numeric, 0::numeric)) AS meituan_discount, sum(COALESCE(NULLIF(bill_records.c097, ''::text)::numeric, 0::numeric)) AS meituan_commission, sum(COALESCE(NULLIF(bill_records.c152, ''::text)::numeric, 0::numeric)) AS taobao_received, sum(COALESCE(NULLIF(bill_records.c102, ''::text)::numeric, 0::numeric)) AS taobao_discount, sum(COALESCE(NULLIF(bill_records.c098, ''::text)::numeric, 0::numeric)) AS taobao_commission, sum(COALESCE(NULLIF(bill_records.c150, ''::text)::numeric, 0::numeric)) AS jd_received, sum(COALESCE(NULLIF(bill_records.c099, ''::text)::numeric, 0::numeric)) AS jd_discount, sum(COALESCE(NULLIF(bill_records.c100, ''::text)::numeric, 0::numeric)) AS jd_commission, round((sum(COALESCE(NULLIF(bill_records.c101, ''::text)::numeric, 0::numeric)) + sum(COALESCE(NULLIF(bill_records.c097, ''::text)::numeric, 0::numeric))) / NULLIF(sum(COALESCE(NULLIF(bill_records.c151, ''::text)::numeric, 0::numeric)) + sum(COALESCE(NULLIF(bill_records.c101, ''::text)::numeric, 0::numeric)) + sum(COALESCE(NULLIF(bill_records.c097, ''::text)::numeric, 0::numeric)), 0::numeric) * 100::numeric, 2) AS meituan_cost_rate_pct, round((sum(COALESCE(NULLIF(bill_records.c102, ''::text)::numeric, 0::numeric)) + sum(COALESCE(NULLIF(bill_records.c098, ''::text)::numeric, 0::numeric))) / NULLIF(sum(COALESCE(NULLIF(bill_records.c152, ''::text)::numeric, 0::numeric)) + sum(COALESCE(NULLIF(bill_records.c102, ''::text)::numeric, 0::numeric)) + sum(COALESCE(NULLIF(bill_records.c098, ''::text)::numeric, 0::numeric)), 0::numeric) * 100::numeric, 2) AS taobao_cost_rate_pct, round((sum(COALESCE(NULLIF(bill_records.c099, ''::text)::numeric, 0::numeric)) + sum(COALESCE(NULLIF(bill_records.c100, ''::text)::numeric, 0::numeric))) / NULLIF(sum(COALESCE(NULLIF(bill_records.c150, ''::text)::numeric, 0::numeric)) + sum(COALESCE(NULLIF(bill_records.c099, ''::text)::numeric, 0::numeric)) + sum(COALESCE(NULLIF(bill_records.c100, ''::text)::numeric, 0::numeric)), 0::numeric) * 100::numeric, 2) AS jd_cost_rate_pct FROM bill_records WHERE NULLIF(bill_records.c005, ''::text) IS NOT NULL GROUP BY (NULLIF(bill_records.c002, ''::text)), (NULLIF(bill_records.c003, ''::text)) WITH DATA; CREATE UNIQUE INDEX IF NOT EXISTS idx_mv_platform_economics_store ON analytics.v_store_platform_economics (store_code); -- 2b. v_store_category_mix DROP VIEW IF EXISTS analytics.v_store_category_mix; CREATE MATERIALIZED VIEW analytics.v_store_category_mix AS SELECT NULLIF(bill_records.c002, ''::text) AS store_code, NULLIF(bill_records.c003, ''::text) AS store_name, sum(COALESCE(NULLIF(bill_records.c009, ''::text)::numeric, 0::numeric)) AS consumption, sum(COALESCE(NULLIF(bill_records.c010, ''::text)::numeric, 0::numeric)) AS lanzhou_noodle, sum(COALESCE(NULLIF(bill_records.c016, ''::text)::numeric, 0::numeric)) AS western_staple, sum(COALESCE(NULLIF(bill_records.c027, ''::text)::numeric, 0::numeric)) AS delivery_package, sum(COALESCE(NULLIF(bill_records.c013, ''::text)::numeric, 0::numeric)) AS night_bbq, sum(COALESCE(NULLIF(bill_records.c015, ''::text)::numeric, 0::numeric)) AS cold_dishes, sum(COALESCE(NULLIF(bill_records.c019, ''::text)::numeric, 0::numeric)) AS silk_road_food, round(sum(COALESCE(NULLIF(bill_records.c010, ''::text)::numeric, 0::numeric)) / NULLIF(sum(COALESCE(NULLIF(bill_records.c009, ''::text)::numeric, 0::numeric)), 0::numeric) * 100::numeric, 2) AS noodle_share_pct, round(sum(COALESCE(NULLIF(bill_records.c027, ''::text)::numeric, 0::numeric)) / NULLIF(sum(COALESCE(NULLIF(bill_records.c009, ''::text)::numeric, 0::numeric)), 0::numeric) * 100::numeric, 2) AS delivery_package_share_pct, round(GREATEST(sum(COALESCE(NULLIF(bill_records.c010, ''::text)::numeric, 0::numeric)), sum(COALESCE(NULLIF(bill_records.c016, ''::text)::numeric, 0::numeric)), sum(COALESCE(NULLIF(bill_records.c027, ''::text)::numeric, 0::numeric)), sum(COALESCE(NULLIF(bill_records.c013, ''::text)::numeric, 0::numeric)), sum(COALESCE(NULLIF(bill_records.c015, ''::text)::numeric, 0::numeric)), sum(COALESCE(NULLIF(bill_records.c019, ''::text)::numeric, 0::numeric))) / NULLIF(sum(COALESCE(NULLIF(bill_records.c009, ''::text)::numeric, 0::numeric)), 0::numeric) * 100::numeric, 2) AS top_category_share_pct, CASE GREATEST(sum(COALESCE(NULLIF(bill_records.c010, ''::text)::numeric, 0::numeric)), sum(COALESCE(NULLIF(bill_records.c016, ''::text)::numeric, 0::numeric)), sum(COALESCE(NULLIF(bill_records.c027, ''::text)::numeric, 0::numeric)), sum(COALESCE(NULLIF(bill_records.c013, ''::text)::numeric, 0::numeric)), sum(COALESCE(NULLIF(bill_records.c015, ''::text)::numeric, 0::numeric)), sum(COALESCE(NULLIF(bill_records.c019, ''::text)::numeric, 0::numeric))) WHEN sum(COALESCE(NULLIF(bill_records.c010, ''::text)::numeric, 0::numeric)) THEN '兰州牛肉面'::text WHEN sum(COALESCE(NULLIF(bill_records.c016, ''::text)::numeric, 0::numeric)) THEN '西部主食'::text WHEN sum(COALESCE(NULLIF(bill_records.c027, ''::text)::numeric, 0::numeric)) THEN '外卖套餐'::text WHEN sum(COALESCE(NULLIF(bill_records.c013, ''::text)::numeric, 0::numeric)) THEN '夜市烧烤'::text WHEN sum(COALESCE(NULLIF(bill_records.c015, ''::text)::numeric, 0::numeric)) THEN '爽口凉菜'::text ELSE '丝路美食'::text END AS top_category FROM bill_records WHERE NULLIF(bill_records.c005, ''::text) IS NOT NULL GROUP BY (NULLIF(bill_records.c002, ''::text)), (NULLIF(bill_records.c003, ''::text)) WITH DATA; CREATE UNIQUE INDEX IF NOT EXISTS idx_mv_category_mix_store ON analytics.v_store_category_mix (store_code); -- 2c. v_store_benchmark DROP VIEW IF EXISTS analytics.v_store_benchmark; CREATE MATERIALIZED VIEW analytics.v_store_benchmark AS WITH eligible AS ( SELECT v_store_risk_rating.store_code, v_store_risk_rating.store_name, v_store_risk_rating.bill_count, v_store_risk_rating.active_days, v_store_risk_rating.received, v_store_risk_rating.avg_daily_received, v_store_risk_rating.avg_bill_value, v_store_risk_rating.avg_guest_value, v_store_risk_rating.discount_rate_pct, v_store_risk_rating.theoretical_margin_pct, v_store_risk_rating.member_bill_share_pct, v_store_risk_rating.anomaly_rate_pct, v_store_risk_rating.risk_level, v_store_risk_rating.primary_issue FROM analytics.v_store_risk_rating WHERE v_store_risk_rating.active_days >= 25 AND v_store_risk_rating.bill_count >= 5000 ), stats AS ( SELECT percentile_cont(0.25::double precision) WITHIN GROUP (ORDER BY (eligible.avg_daily_received::double precision)) AS revenue_p25, percentile_cont(0.50::double precision) WITHIN GROUP (ORDER BY (eligible.avg_daily_received::double precision)) AS revenue_p50, percentile_cont(0.75::double precision) WITHIN GROUP (ORDER BY (eligible.avg_daily_received::double precision)) AS revenue_p75, percentile_cont(0.50::double precision) WITHIN GROUP (ORDER BY (eligible.theoretical_margin_pct::double precision)) AS margin_p50, percentile_cont(0.75::double precision) WITHIN GROUP (ORDER BY (eligible.theoretical_margin_pct::double precision)) AS margin_p75, percentile_cont(0.75::double precision) WITHIN GROUP (ORDER BY (eligible.discount_rate_pct::double precision)) AS discount_p75 FROM eligible ), base AS ( SELECT e.store_code, e.store_name, e.bill_count, e.active_days, e.received, e.avg_daily_received, e.avg_bill_value, e.avg_guest_value, e.discount_rate_pct, e.theoretical_margin_pct, e.member_bill_share_pct, e.anomaly_rate_pct, e.risk_level, e.primary_issue, s.revenue_p25, s.revenue_p50, s.revenue_p75, s.margin_p50, s.margin_p75, s.discount_p75 FROM eligible e CROSS JOIN stats s ) SELECT base.store_code, base.store_name, base.bill_count, base.active_days, base.received, base.avg_daily_received, base.avg_bill_value, base.avg_guest_value, base.discount_rate_pct, base.theoretical_margin_pct, base.member_bill_share_pct, base.anomaly_rate_pct, base.risk_level, base.primary_issue, base.revenue_p25, base.revenue_p50, base.revenue_p75, base.margin_p50, base.margin_p75, base.discount_p75, CASE WHEN base.avg_daily_received::double precision >= base.revenue_p75 AND base.theoretical_margin_pct::double precision >= base.margin_p50 THEN '明星门店'::text WHEN base.avg_daily_received::double precision >= base.revenue_p75 AND base.theoretical_margin_pct::double precision < base.margin_p50 THEN '规模承压'::text WHEN base.avg_daily_received::double precision < base.revenue_p75 AND base.theoretical_margin_pct::double precision >= base.margin_p75 THEN '高效潜力'::text WHEN base.avg_daily_received::double precision <= base.revenue_p25 OR base.theoretical_margin_pct < 68::numeric OR base.discount_rate_pct::double precision > base.discount_p75 THEN '重点改善'::text ELSE '稳健经营'::text END AS management_quadrant, round(GREATEST(base.received / NULLIF(1::numeric - base.discount_rate_pct / 100::numeric, 0::numeric) * (base.discount_rate_pct - 20.23) / 100::numeric, 0::numeric), 2) AS discount_saving_to_company_avg, round(GREATEST(base.received * 0.7092 - base.received * base.theoretical_margin_pct / 100::numeric, 0::numeric), 2) AS profit_uplift_to_company_margin FROM base WITH DATA; CREATE UNIQUE INDEX IF NOT EXISTS idx_mv_benchmark_store ON analytics.v_store_benchmark (store_code); -- ============================================================ -- Step 3: 组合视图 → 物化视图 -- ============================================================ -- 3a. v_store_action_list (joins benchmark + category_mix + platform_economics) CREATE MATERIALIZED VIEW analytics.v_store_action_list AS SELECT b.store_code, b.store_name, b.management_quadrant, b.risk_level, b.primary_issue, b.received, b.avg_daily_received, b.avg_bill_value, b.discount_rate_pct, b.theoretical_margin_pct, b.anomaly_rate_pct, b.member_bill_share_pct, b.discount_saving_to_company_avg, b.profit_uplift_to_company_margin, c.top_category, c.top_category_share_pct, c.delivery_package_share_pct, p.meituan_cost_rate_pct, p.taobao_cost_rate_pct, p.jd_cost_rate_pct FROM analytics.v_store_benchmark b LEFT JOIN analytics.v_store_category_mix c USING (store_code, store_name) LEFT JOIN analytics.v_store_platform_economics p USING (store_code, store_name) WITH DATA; CREATE UNIQUE INDEX IF NOT EXISTS idx_mv_action_list_store ON analytics.v_store_action_list (store_code); -- 3b. v_store_execution_priority (joins action_list + meal + member + zero_bill) CREATE MATERIALIZED VIEW analytics.v_store_execution_priority AS WITH meal AS ( SELECT v_store_meal_opportunity.store_code, sum(v_store_meal_opportunity.avg_bill_uplift_scenario) AS meal_uplift_scenario FROM analytics.v_store_meal_opportunity WHERE v_store_meal_opportunity.bill_count >= 500 GROUP BY v_store_meal_opportunity.store_code ), zero_bill AS ( SELECT v_zero_received_detail.store_code, count(*) AS zero_received_bills, count(*) FILTER (WHERE v_zero_received_detail.zero_received_type = '优惠不足但无实收'::text) AS unexplained_zero_bills FROM analytics.v_zero_received_detail GROUP BY v_zero_received_detail.store_code ) SELECT a.store_code, a.store_name, a.management_quadrant, a.risk_level, a.primary_issue, a.received, a.avg_daily_received, a.avg_bill_value, a.discount_rate_pct, a.theoretical_margin_pct, a.anomaly_rate_pct, a.member_bill_share_pct, a.discount_saving_to_company_avg, a.profit_uplift_to_company_margin, a.top_category, a.top_category_share_pct, a.delivery_package_share_pct, a.meituan_cost_rate_pct, a.taobao_cost_rate_pct, a.jd_cost_rate_pct, round(COALESCE(m.meal_uplift_scenario, 0::numeric), 2) AS meal_uplift_scenario, mo.member_share_pct, mo.conversion_bill_scenario, mo.revenue_uplift_scenario AS member_revenue_uplift_scenario, COALESCE(z.zero_received_bills, 0::bigint) AS zero_received_bills, COALESCE(z.unexplained_zero_bills, 0::bigint) AS unexplained_zero_bills FROM analytics.v_store_action_list a LEFT JOIN meal m USING (store_code) LEFT JOIN analytics.v_store_member_opportunity mo USING (store_code, store_name) LEFT JOIN zero_bill z USING (store_code) WITH DATA; CREATE UNIQUE INDEX IF NOT EXISTS idx_mv_execution_priority_store ON analytics.v_store_execution_priority (store_code); -- ============================================================ -- Step 4: 重建依赖普通视图(基于物化视图,查询会走索引) -- ============================================================ -- 4a. v_store_benchmark_composite CREATE OR REPLACE VIEW analytics.v_store_benchmark_composite AS WITH eligible AS ( SELECT b.store_code, b.store_name, b.received, b.avg_daily_received, b.avg_bill_value, b.discount_rate_pct, b.theoretical_margin_pct, b.anomaly_rate_pct, b.member_bill_share_pct, r.identified_members, r.repeat_rate_pct, r.avg_orders, r.repeat_revenue_share_pct, p.meituan_cost_rate_pct, p.taobao_cost_rate_pct, p.jd_cost_rate_pct FROM analytics.v_store_benchmark b JOIN analytics.v_store_repeat_summary_monthly r ON r.store_code = b.store_code AND r.month_start = '2026-04-01'::date LEFT JOIN analytics.v_store_platform_economics p ON p.store_code = b.store_code WHERE b.theoretical_margin_pct >= 60::numeric AND b.theoretical_margin_pct <= 80::numeric AND r.identified_members >= 500 ), scored AS ( SELECT eligible.store_code, eligible.store_name, eligible.received, eligible.avg_daily_received, eligible.avg_bill_value, eligible.discount_rate_pct, eligible.theoretical_margin_pct, eligible.anomaly_rate_pct, eligible.member_bill_share_pct, eligible.identified_members, eligible.repeat_rate_pct, eligible.avg_orders, eligible.repeat_revenue_share_pct, eligible.meituan_cost_rate_pct, eligible.taobao_cost_rate_pct, eligible.jd_cost_rate_pct, percent_rank() OVER (ORDER BY eligible.avg_daily_received) AS revenue_score, percent_rank() OVER (ORDER BY eligible.theoretical_margin_pct) AS margin_score, 1::double precision - percent_rank() OVER (ORDER BY eligible.discount_rate_pct) AS discount_score, 1::double precision - percent_rank() OVER (ORDER BY eligible.anomaly_rate_pct) AS anomaly_score, percent_rank() OVER (ORDER BY eligible.repeat_rate_pct) AS repeat_score FROM eligible ) SELECT scored.store_code, scored.store_name, scored.received, scored.avg_daily_received, scored.avg_bill_value, scored.discount_rate_pct, scored.theoretical_margin_pct, scored.anomaly_rate_pct, scored.member_bill_share_pct, scored.identified_members, scored.repeat_rate_pct, scored.avg_orders, scored.repeat_revenue_share_pct, scored.meituan_cost_rate_pct, scored.taobao_cost_rate_pct, scored.jd_cost_rate_pct, scored.revenue_score, scored.margin_score, scored.discount_score, scored.anomaly_score, scored.repeat_score, round(((scored.revenue_score * 0.25::double precision + scored.margin_score * 0.25::double precision + scored.discount_score * 0.20::double precision + scored.anomaly_score * 0.15::double precision + scored.repeat_score * 0.15::double precision) * 100::double precision)::numeric, 2) AS benchmark_score FROM scored; -- 4b. v_store_deep_diagnosis_april CREATE OR REPLACE VIEW analytics.v_store_deep_diagnosis_april AS WITH store_base AS ( SELECT s.store_code, s.store_name, s.bill_count, s.active_days, s.received, s.avg_daily_received, s.avg_bill_value, s.discount_rate_pct, s.theoretical_margin_pct, s.member_bill_share_pct, d.items_per_bill, d.skus_per_bill, d.delivery_bill_share_pct, d.noodle_snack_attach_pct, d.noodle_drink_attach_pct, d.noodle_cold_attach_pct, d.combo_bill_share_pct, c.theoretical_cost, c.actual_food_cost, c.food_cost_variance, c.theoretical_cost_rate_pct, c.actual_food_cost_rate_pct, c.variance_to_theoretical_pct, c.comparison_status, c.variance_level, b.identified_members, b.repeat_rate_pct, b.repeat_revenue_share_pct, b.benchmark_score, p.meituan_received, p.taobao_received, p.jd_received, round((COALESCE(p.meituan_discount, 0::numeric) + COALESCE(p.taobao_discount, 0::numeric) + COALESCE(p.jd_discount, 0::numeric) + COALESCE(p.meituan_commission, 0::numeric) + COALESCE(p.taobao_commission, 0::numeric) + COALESCE(p.jd_commission, 0::numeric)) / NULLIF(COALESCE(p.meituan_received, 0::numeric) + COALESCE(p.taobao_received, 0::numeric) + COALESCE(p.jd_received, 0::numeric) + COALESCE(p.meituan_discount, 0::numeric) + COALESCE(p.taobao_discount, 0::numeric) + COALESCE(p.jd_discount, 0::numeric) + COALESCE(p.meituan_commission, 0::numeric) + COALESCE(p.taobao_commission, 0::numeric) + COALESCE(p.jd_commission, 0::numeric), 0::numeric) * 100::numeric, 2) AS combined_platform_cost_rate_pct, CASE WHEN s.store_name ~ '机场|火锅|商城|快手|哈马尔罕'::text THEN '特殊业态'::text ELSE '标准门店'::text END AS business_type FROM analytics.v_store_scorecard s LEFT JOIN analytics.dish_store_summary_april d USING (store_code) LEFT JOIN analytics.v_store_theoretical_actual_cost_april c USING (store_code) LEFT JOIN analytics.v_store_benchmark_composite b USING (store_code) LEFT JOIN analytics.v_store_platform_economics p USING (store_code) ), tiered AS ( SELECT store_base.store_code, store_base.store_name, store_base.bill_count, store_base.active_days, store_base.received, store_base.avg_daily_received, store_base.avg_bill_value, store_base.discount_rate_pct, store_base.theoretical_margin_pct, store_base.member_bill_share_pct, store_base.items_per_bill, store_base.skus_per_bill, store_base.delivery_bill_share_pct, store_base.noodle_snack_attach_pct, store_base.noodle_drink_attach_pct, store_base.noodle_cold_attach_pct, store_base.combo_bill_share_pct, store_base.theoretical_cost, store_base.actual_food_cost, store_base.food_cost_variance, store_base.theoretical_cost_rate_pct, store_base.actual_food_cost_rate_pct, store_base.variance_to_theoretical_pct, store_base.comparison_status, store_base.variance_level, store_base.identified_members, store_base.repeat_rate_pct, store_base.repeat_revenue_share_pct, store_base.benchmark_score, store_base.meituan_received, store_base.taobao_received, store_base.jd_received, store_base.combined_platform_cost_rate_pct, store_base.business_type, CASE WHEN store_base.business_type = '特殊业态'::text THEN '特殊业态'::text WHEN percent_rank() OVER (PARTITION BY store_base.business_type ORDER BY store_base.received) >= 0.67::double precision THEN '高规模'::text WHEN percent_rank() OVER (PARTITION BY store_base.business_type ORDER BY store_base.received) >= 0.33::double precision THEN '中规模'::text ELSE '低规模'::text END AS scale_tier FROM store_base ) SELECT tiered.store_code, tiered.store_name, tiered.bill_count, tiered.active_days, tiered.received, tiered.avg_daily_received, tiered.avg_bill_value, tiered.discount_rate_pct, tiered.theoretical_margin_pct, tiered.member_bill_share_pct, tiered.items_per_bill, tiered.skus_per_bill, tiered.delivery_bill_share_pct, tiered.noodle_snack_attach_pct, tiered.noodle_drink_attach_pct, tiered.noodle_cold_attach_pct, tiered.combo_bill_share_pct, tiered.theoretical_cost, tiered.actual_food_cost, tiered.food_cost_variance, tiered.theoretical_cost_rate_pct, tiered.actual_food_cost_rate_pct, tiered.variance_to_theoretical_pct, tiered.comparison_status, tiered.variance_level, tiered.identified_members, tiered.repeat_rate_pct, tiered.repeat_revenue_share_pct, tiered.benchmark_score, tiered.meituan_received, tiered.taobao_received, tiered.jd_received, tiered.combined_platform_cost_rate_pct, tiered.business_type, tiered.scale_tier, CASE WHEN tiered.comparison_status = '可比'::text AND tiered.variance_to_theoretical_pct >= 20::numeric THEN 1 ELSE 0 END + CASE WHEN tiered.discount_rate_pct >= 23.02 THEN 1 ELSE 0 END + CASE WHEN tiered.theoretical_margin_pct < 70::numeric THEN 1 ELSE 0 END + CASE WHEN tiered.identified_members >= 500 AND tiered.repeat_rate_pct < 30::numeric THEN 1 ELSE 0 END + CASE WHEN tiered.combined_platform_cost_rate_pct >= 40::numeric THEN 1 ELSE 0 END + CASE WHEN tiered.noodle_drink_attach_pct < 12::numeric THEN 1 ELSE 0 END AS problem_count, concat_ws('+'::text, CASE WHEN tiered.comparison_status = '理论成本口径异常'::text THEN '成本口径异常'::text ELSE NULL::text END, CASE WHEN tiered.comparison_status = '可比'::text AND tiered.variance_to_theoretical_pct >= 20::numeric THEN '实际成本严重超耗'::text ELSE NULL::text END, CASE WHEN tiered.discount_rate_pct >= 23.02 THEN '优惠偏高'::text ELSE NULL::text END, CASE WHEN tiered.theoretical_margin_pct < 70::numeric THEN '理论毛利偏低'::text ELSE NULL::text END, CASE WHEN tiered.identified_members >= 500 AND tiered.repeat_rate_pct < 30::numeric THEN '会员复购偏低'::text ELSE NULL::text END, CASE WHEN tiered.combined_platform_cost_rate_pct >= 40::numeric THEN '平台成本偏高'::text ELSE NULL::text END, CASE WHEN tiered.noodle_drink_attach_pct < 12::numeric THEN '饮品搭售偏低'::text ELSE NULL::text END) AS problem_combination FROM tiered; -- ============================================================ -- Step 5: 验证 -- ============================================================ SELECT 'Materialized views created successfully' AS status;