85 lines
3.8 KiB
PL/PgSQL
85 lines
3.8 KiB
PL/PgSQL
-- ============================================================
|
||
-- Phase 8: mv_store_risk_rating 参数化函数
|
||
-- 替换普通表 mv_store_risk_rating(4月快照)为参数化函数
|
||
-- 依赖: 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;
|