Files
SBrainCO/db/migration_phase8_risk_rating.sql
T

85 lines
3.8 KiB
PL/PgSQL
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.
-- ============================================================
-- Phase 8: mv_store_risk_rating 参数化函数
-- 替换普通表 mv_store_risk_rating4月快照)为参数化函数
-- 依赖: bill_fact, v_anomaly_bills
-- ============================================================
CREATE OR REPLACE FUNCTION analytics.fn_store_risk_rating(p_month date)
RETURNS TABLE (
store_code text,
store_name text,
bill_count bigint,
active_days bigint,
received numeric,
avg_daily_received numeric,
avg_bill_value numeric,
avg_guest_value numeric,
discount_rate_pct numeric,
theoretical_margin_pct numeric,
member_bill_share_pct numeric,
anomaly_rate_pct numeric,
risk_level text,
primary_issue text
) AS $$
WITH base AS (
SELECT
b.store_code,
b.store_name,
count(*) AS bill_count,
count(DISTINCT b.closed_at::date) AS active_days,
sum(b.received_total) AS received,
round(sum(b.received_total) / NULLIF(count(DISTINCT b.closed_at::date), 0), 2) AS avg_daily_received,
round(sum(b.received_total) / count(*), 2) AS avg_bill_value,
round(sum(b.received_total) / NULLIF(sum(b.guest_count), 0), 2) AS avg_guest_value,
round(sum(b.discount_total) / NULLIF(sum(b.consumption), 0) * 100, 2) AS discount_rate_pct,
round(sum(b.theoretical_profit) / NULLIF(sum(b.received_total), 0) * 100, 2) AS theoretical_margin_pct,
round(count(*) FILTER (WHERE b.member_id IS NOT NULL)::numeric / count(*) * 100, 2) AS member_bill_share_pct
FROM analytics.bill_fact b
WHERE b.closed_at >= analytics.fn_month_start_ts(p_month)
AND b.closed_at < analytics.fn_next_month_start_ts(p_month)
GROUP BY b.store_code, b.store_name
),
anomaly AS (
SELECT
a.store_code,
round(count(*)::numeric / NULLIF(bc.bill_count, 0) * 100, 2) AS anomaly_rate_pct
FROM analytics.v_anomaly_bills a
JOIN (SELECT store_code, count(*) AS bill_count FROM analytics.bill_fact
WHERE closed_at >= analytics.fn_month_start_ts(p_month)
AND closed_at < analytics.fn_next_month_start_ts(p_month)
GROUP BY store_code) bc ON a.store_code = bc.store_code
WHERE a.closed_at >= analytics.fn_month_start_ts(p_month)
AND a.closed_at < analytics.fn_next_month_start_ts(p_month)
GROUP BY a.store_code, bc.bill_count
),
combined AS (
SELECT
b.*,
COALESCE(an.anomaly_rate_pct, 0) AS anomaly_rate_pct,
CASE
WHEN b.received IS NULL OR b.received = 0 THEN '绿色'
WHEN b.theoretical_margin_pct < 68 OR b.discount_rate_pct > 25 OR COALESCE(an.anomaly_rate_pct, 0) > 3 THEN '红色'
WHEN b.theoretical_margin_pct >= 70 AND b.discount_rate_pct <= 22 AND COALESCE(an.anomaly_rate_pct, 0) <= 1.5 THEN '绿色'
ELSE '黄色'
END AS risk_level,
CASE
WHEN b.theoretical_margin_pct < 68 AND b.discount_rate_pct > 25 AND COALESCE(an.anomaly_rate_pct, 0) > 3 THEN '毛利率低;优惠率高;异常率高'
WHEN b.theoretical_margin_pct < 68 AND b.discount_rate_pct > 25 THEN '毛利率低;优惠率高'
WHEN b.theoretical_margin_pct < 68 AND COALESCE(an.anomaly_rate_pct, 0) > 3 THEN '毛利率低;异常率高'
WHEN b.discount_rate_pct > 25 AND COALESCE(an.anomaly_rate_pct, 0) > 3 THEN '优惠率高;异常率高'
WHEN b.theoretical_margin_pct < 68 THEN '毛利率低'
WHEN b.discount_rate_pct > 25 THEN '优惠率高'
WHEN COALESCE(an.anomaly_rate_pct, 0) > 3 THEN '异常率高'
ELSE ''
END AS primary_issue
FROM base b
LEFT JOIN anomaly an ON b.store_code = an.store_code
)
SELECT
store_code, store_name, bill_count, active_days, received,
avg_daily_received, avg_bill_value, avg_guest_value,
discount_rate_pct, theoretical_margin_pct, member_bill_share_pct,
anomaly_rate_pct, risk_level, primary_issue
FROM combined
$$ LANGUAGE SQL STABLE;