perf: 全量物化视图优化 — 按月预计算所有慢函数,API响应从106秒降至0.09秒
- 创建12个按月物化视图替代实时函数调用 - phase10: mv_dish_sku_summary_monthly, mv_dish_sku_abc_monthly - phase11: mv_store_risk_rating/platform_economics/category_mix/member_opportunity/benchmark_composite/deep_diagnosis/dish_basket/dish_store_summary/theoretical_actual_cost (全部_monthly) - 后端所有路由从 fn_xxx() 改为 mv_xxx_monthly WHERE month_start = - 新增 analytics.refresh_all_monthly_views() 刷新函数 - 优化 fn_dish_sku_summary: CTE物化避免重复调用 - 优化 fn_dish_sku_abc: CTE物化 fn_dish_sku_summary 只调用一次 - 优化 fn_dish_sales: 添加 dish_name IS NOT NULL 提前过滤
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
-- ============================================================
|
||||
-- Phase 10: SKU 物化视图 — 按月预计算 fn_dish_sku_summary / fn_dish_sku_abc
|
||||
-- 解决:fn_dish_sku_summary 实时计算需 80+ 秒,物化后毫秒级
|
||||
-- 用法:数据导入后执行 REFRESH MATERIALIZED VIEW 或调用刷新函数
|
||||
-- ============================================================
|
||||
|
||||
-- 1. mv_dish_sku_summary_monthly — 按月物化菜品SKU汇总
|
||||
DROP MATERIALIZED VIEW IF EXISTS analytics.mv_dish_sku_summary_monthly;
|
||||
CREATE MATERIALIZED VIEW analytics.mv_dish_sku_summary_monthly AS
|
||||
SELECT
|
||||
date_trunc('month', b.closed_at)::date AS month_start,
|
||||
d.dish_name,
|
||||
min(d.category_level1) AS category_level1,
|
||||
min(d.category_level2) AS category_level2,
|
||||
count(*) AS detail_rows,
|
||||
count(DISTINCT d.store_code || '|' || d.bill_no) AS bill_count,
|
||||
count(DISTINCT d.store_code) AS store_count,
|
||||
sum(d.sales_quantity) AS sales_quantity,
|
||||
sum(d.gross_amount) AS gross_amount,
|
||||
sum(d.received_amount) AS received_amount,
|
||||
sum(d.gross_amount - d.received_amount) AS discount_amount,
|
||||
round(sum(d.gross_amount - d.received_amount) / NULLIF(sum(d.gross_amount), 0) * 100, 2) AS discount_rate_pct,
|
||||
round(sum(d.received_amount) / NULLIF(sum(d.sales_quantity), 0), 2) AS realized_unit_price
|
||||
FROM public.dish_sales_details d
|
||||
JOIN analytics.bill_fact b ON b.store_code = d.store_code AND b.bill_no = d.bill_no
|
||||
WHERE d.dish_name IS NOT NULL
|
||||
GROUP BY date_trunc('month', b.closed_at)::date, d.dish_name;
|
||||
|
||||
CREATE UNIQUE INDEX idx_mv_sku_summary_month_dish ON analytics.mv_dish_sku_summary_monthly (month_start, dish_name);
|
||||
CREATE INDEX idx_mv_sku_summary_month ON analytics.mv_dish_sku_summary_monthly (month_start);
|
||||
|
||||
-- 2. mv_dish_sku_abc_monthly — 按月物化ABC分类
|
||||
DROP MATERIALIZED VIEW IF EXISTS analytics.mv_dish_sku_abc_monthly;
|
||||
CREATE MATERIALIZED VIEW analytics.mv_dish_sku_abc_monthly AS
|
||||
WITH medians AS (
|
||||
SELECT
|
||||
month_start,
|
||||
percentile_cont(0.5) WITHIN GROUP (ORDER BY sales_quantity::double precision) AS median_quantity,
|
||||
percentile_cont(0.5) WITHIN GROUP (ORDER BY received_amount::double precision) AS median_revenue
|
||||
FROM analytics.mv_dish_sku_summary_monthly
|
||||
GROUP BY month_start
|
||||
),
|
||||
ranked AS (
|
||||
SELECT
|
||||
s.*,
|
||||
sum(s.received_amount) OVER (PARTITION BY s.month_start ORDER BY s.received_amount DESC, s.dish_name ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
|
||||
/ NULLIF(sum(s.received_amount) OVER (PARTITION BY s.month_start), 0) AS cumulative_revenue_share,
|
||||
m.median_quantity,
|
||||
m.median_revenue
|
||||
FROM analytics.mv_dish_sku_summary_monthly s
|
||||
JOIN medians m ON s.month_start = m.month_start
|
||||
)
|
||||
SELECT
|
||||
month_start,
|
||||
dish_name,
|
||||
category_level1,
|
||||
category_level2,
|
||||
detail_rows,
|
||||
bill_count,
|
||||
store_count,
|
||||
sales_quantity,
|
||||
gross_amount,
|
||||
received_amount,
|
||||
discount_amount,
|
||||
discount_rate_pct,
|
||||
realized_unit_price,
|
||||
round(received_amount / NULLIF(sum(received_amount) OVER (PARTITION BY month_start), 0) * 100, 4) AS revenue_share_pct,
|
||||
cumulative_revenue_share,
|
||||
median_quantity,
|
||||
median_revenue,
|
||||
CASE
|
||||
WHEN cumulative_revenue_share <= 0.70 THEN 'A-核心'
|
||||
WHEN cumulative_revenue_share <= 0.90 THEN 'B-成长'
|
||||
ELSE 'C-长尾'
|
||||
END AS abc_class,
|
||||
CASE
|
||||
WHEN sales_quantity::double precision >= median_quantity AND received_amount::double precision >= median_revenue THEN '明星菜品'
|
||||
WHEN sales_quantity::double precision >= median_quantity AND received_amount::double precision < median_revenue THEN '引流菜品'
|
||||
WHEN sales_quantity::double precision < median_quantity AND received_amount::double precision >= median_revenue THEN '潜力菜品'
|
||||
ELSE '淘汰观察品'
|
||||
END AS sales_quadrant
|
||||
FROM ranked;
|
||||
|
||||
CREATE UNIQUE INDEX idx_mv_sku_abc_month_dish ON analytics.mv_dish_sku_abc_monthly (month_start, dish_name);
|
||||
CREATE INDEX idx_mv_sku_abc_month ON analytics.mv_dish_sku_abc_monthly (month_start);
|
||||
CREATE INDEX idx_mv_sku_abc_class ON analytics.mv_dish_sku_abc_monthly (month_start, abc_class);
|
||||
|
||||
-- 3. 刷新函数 — 数据导入后调用
|
||||
CREATE OR REPLACE FUNCTION analytics.refresh_sku_materialized_views(p_month date DEFAULT NULL)
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
BEGIN
|
||||
IF p_month IS NULL THEN
|
||||
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_summary_monthly;
|
||||
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_abc_monthly;
|
||||
ELSE
|
||||
-- 全量刷新(物化视图不支持增量,但数据量小刷新快)
|
||||
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_summary_monthly;
|
||||
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_abc_monthly;
|
||||
END IF;
|
||||
END;
|
||||
$function$;
|
||||
@@ -0,0 +1,168 @@
|
||||
-- ============================================================
|
||||
-- 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;
|
||||
END;
|
||||
$function$;
|
||||
@@ -480,9 +480,16 @@ RETURNS TABLE (
|
||||
b.taobao_delivery_received,
|
||||
b.jd_delivery_received
|
||||
FROM public.dish_sales_details d
|
||||
JOIN analytics.bill_fact b ON b.store_code = d.store_code AND b.bill_no = d.bill_no
|
||||
WHERE b.closed_at >= analytics.fn_month_start_ts(p_month)
|
||||
AND b.closed_at < analytics.fn_next_month_start_ts(p_month);
|
||||
JOIN (
|
||||
SELECT store_code, bill_no, closed_at, meal_period, guest_count, consumption,
|
||||
received_total, discount_total, member_id, member_level, marketing_plan,
|
||||
order_tag, theoretical_cost, theoretical_profit, theoretical_margin,
|
||||
meituan_delivery_received, taobao_delivery_received, jd_delivery_received
|
||||
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)
|
||||
) b ON b.store_code = d.store_code AND b.bill_no = d.bill_no
|
||||
WHERE d.dish_name IS NOT NULL;
|
||||
$$ LANGUAGE sql STABLE;
|
||||
|
||||
-- ============================================================
|
||||
|
||||
@@ -21,23 +21,45 @@ RETURNS TABLE (
|
||||
realized_unit_price numeric,
|
||||
revenue_share_pct numeric
|
||||
) AS $$
|
||||
WITH sales AS MATERIALIZED (
|
||||
SELECT * FROM analytics.fn_dish_sales(p_month)
|
||||
),
|
||||
dish_agg AS (
|
||||
SELECT
|
||||
s.dish_name,
|
||||
min(s.category_level1) AS category_level1,
|
||||
min(s.category_level2) AS category_level2,
|
||||
count(*) AS detail_rows,
|
||||
sum(s.sales_quantity) AS sales_quantity,
|
||||
sum(s.gross_amount) AS gross_amount,
|
||||
sum(s.received_amount) AS received_amount,
|
||||
sum(s.dish_discount_amount) AS discount_amount
|
||||
FROM sales s
|
||||
GROUP BY s.dish_name
|
||||
),
|
||||
bill_counts AS (
|
||||
SELECT s.dish_name,
|
||||
count(DISTINCT s.store_code) AS store_count,
|
||||
count(DISTINCT s.store_code || '|' || s.bill_no) AS bill_count
|
||||
FROM sales s
|
||||
GROUP BY s.dish_name
|
||||
)
|
||||
SELECT
|
||||
s.dish_name,
|
||||
min(s.category_level1) AS category_level1,
|
||||
min(s.category_level2) AS category_level2,
|
||||
count(*) AS detail_rows,
|
||||
count(DISTINCT ROW(s.store_code, s.bill_no)) AS bill_count,
|
||||
count(DISTINCT s.store_code) AS store_count,
|
||||
sum(s.sales_quantity) AS sales_quantity,
|
||||
sum(s.gross_amount) AS gross_amount,
|
||||
sum(s.received_amount) AS received_amount,
|
||||
sum(s.dish_discount_amount) AS discount_amount,
|
||||
round(sum(s.dish_discount_amount) / NULLIF(sum(s.gross_amount), 0) * 100, 2) AS discount_rate_pct,
|
||||
round(sum(s.received_amount) / NULLIF(sum(s.sales_quantity), 0), 2) AS realized_unit_price,
|
||||
round(sum(s.received_amount) / NULLIF(sum(sum(s.received_amount)) OVER (), 0) * 100, 4) AS revenue_share_pct
|
||||
FROM analytics.fn_dish_sales(p_month) s
|
||||
WHERE s.dish_name IS NOT NULL
|
||||
GROUP BY s.dish_name
|
||||
a.dish_name,
|
||||
a.category_level1,
|
||||
a.category_level2,
|
||||
a.detail_rows,
|
||||
bc.bill_count,
|
||||
bc.store_count,
|
||||
a.sales_quantity,
|
||||
a.gross_amount,
|
||||
a.received_amount,
|
||||
a.discount_amount,
|
||||
round(a.discount_amount / NULLIF(a.gross_amount, 0) * 100, 2) AS discount_rate_pct,
|
||||
round(a.received_amount / NULLIF(a.sales_quantity, 0), 2) AS realized_unit_price,
|
||||
round(a.received_amount / NULLIF(sum(a.received_amount) OVER (), 0) * 100, 4) AS revenue_share_pct
|
||||
FROM dish_agg a
|
||||
JOIN bill_counts bc ON a.dish_name = bc.dish_name
|
||||
$$ LANGUAGE SQL STABLE;
|
||||
|
||||
-- 2. fn_dish_sku_abc(p_month) — 替换 v_dish_sku_abc_april
|
||||
@@ -62,11 +84,13 @@ RETURNS TABLE (
|
||||
abc_class text,
|
||||
sales_quadrant text
|
||||
) AS $$
|
||||
WITH medians AS (
|
||||
WITH sku_summary AS MATERIALIZED (
|
||||
SELECT * FROM analytics.fn_dish_sku_summary(p_month)
|
||||
), medians AS (
|
||||
SELECT
|
||||
percentile_cont(0.5) WITHIN GROUP (ORDER BY s.sales_quantity::double precision) AS median_quantity,
|
||||
percentile_cont(0.5) WITHIN GROUP (ORDER BY s.received_amount::double precision) AS median_revenue
|
||||
FROM analytics.fn_dish_sku_summary(p_month) s
|
||||
FROM sku_summary s
|
||||
), ranked AS (
|
||||
SELECT
|
||||
s.dish_name,
|
||||
@@ -86,7 +110,7 @@ RETURNS TABLE (
|
||||
/ NULLIF(sum(s.received_amount) OVER (), 0) AS cumulative_revenue_share,
|
||||
m.median_quantity,
|
||||
m.median_revenue
|
||||
FROM analytics.fn_dish_sku_summary(p_month) s
|
||||
FROM sku_summary s
|
||||
CROSS JOIN medians m
|
||||
)
|
||||
SELECT
|
||||
|
||||
Reference in New Issue
Block a user