CREATE VIEW v_user_profile AS SELECT u.username, u.city, u.is_vip, COUNT(o.id) AS order_count, COALESCE(SUM(o.total), 0) AS total_spent FROM users u LEFT JOIN orders o ON o.user_id = u.id GROUP BY u.id, u.username, u.city, u.is_vip;
SELECT * FROM v_user_profile WHERE city = '北京' ORDER BY total_spent DESC LIMIT 1;
CREATE MATERIALIZED VIEW mv_category_stats AS SELECT c.name AS category, COUNT(p.id) AS product_count, ROUND(AVG(p.price), 2) AS avg_price, SUM(p.stock) AS total_stock FROM categories c LEFT JOIN products p ON p.category_id = c.id GROUP BY c.id, c.name;
SELECT city, username, total_spent FROM ( SELECT u.city, u.username, SUM(o.total) AS total_spent, RANK() OVER (PARTITION BY u.city ORDER BY SUM(o.total) DESC) AS rnk FROM users u JOIN orders o ON o.user_id = u.id WHERE o.status != 'cancelled' GROUP BY u.id, u.city, u.username ) ranked WHERE rnk = 1;
WITH elec_buyers AS ( SELECT o.user_id, COUNT(DISTINCT o.id) AS elec_order_count FROM orders o JOIN order_items oi ON oi.order_id = o.id JOIN products p ON p.id = oi.product_id WHERE p.category_id = 1 GROUP BY o.user_id ) SELECT COUNT(*) AS total_elec_buyers, COUNT(*) FILTER (WHERE elec_order_count >= 2) AS repeat_buyers, ROUND( COUNT(*) FILTER (WHERE elec_order_count >= 2)::numeric / COUNT(*) * 100, 1 ) AS repeat_rate_pct FROM elec_buyers;
CREATE VIEW v_rfm AS SELECT u.username, u.city, CURRENT_DATE - MAX(o.created_at)::date AS recency_days, COUNT(o.id) AS frequency, COALESCE(SUM(o.total), 0) AS monetary FROM users u LEFT JOIN orders o ON o.user_id = u.id AND o.status != 'cancelled' GROUP BY u.id, u.username, u.city;
SELECT * FROM v_rfm ORDER BY monetary DESC;
WITH user_level AS ( SELECT u.username, COALESCE(SUM(o.total), 0) AS total_spent, CASE WHEN COALESCE(SUM(o.total), 0) >= 10000 THEN '钻石' WHEN COALESCE(SUM(o.total), 0) >= 5000 THEN '黄金' WHEN COALESCE(SUM(o.total), 0) >= 1000 THEN '白银' ELSE '青铜' END AS level FROM users u LEFT JOIN orders o ON o.user_id = u.id AND o.status != 'cancelled' GROUP BY u.id, u.username ) SELECT level, COUNT(*) AS user_count, ROUND(COUNT(*)::numeric / (SELECT COUNT(*) FROM users) * 100, 1) AS pct FROM user_level GROUP BY level ORDER BY CASE level WHEN '钻石' THEN 1 WHEN '黄金' THEN 2 WHEN '白银' THEN 3 ELSE 4 END;
WITH alice_products AS ( SELECT DISTINCT oi.product_id FROM orders o JOIN order_items oi ON oi.order_id = o.id WHERE o.user_id = 1 ), similar_users AS ( SELECT DISTINCT o.user_id FROM orders o JOIN order_items oi ON oi.order_id = o.id WHERE oi.product_id IN (SELECT product_id FROM alice_products) AND o.user_id != 1 ) SELECT p.name AS recommended_product, COUNT(DISTINCT o.user_id) AS bought_by_n_users FROM orders o JOIN order_items oi ON oi.order_id = o.id JOIN products p ON p.id = oi.product_id WHERE o.user_id IN (SELECT user_id FROM similar_users) AND oi.product_id NOT IN (SELECT product_id FROM alice_products) GROUP BY p.id, p.name ORDER BY bought_by_n_users DESC;
WITH months AS ( SELECT DISTINCT TO_CHAR(created_at, 'YYYY-MM') AS month FROM orders ), user_months AS ( SELECT user_id, TO_CHAR(created_at, 'YYYY-MM') AS month FROM orders WHERE status != 'cancelled' GROUP BY user_id, TO_CHAR(created_at, 'YYYY-MM') ), first_order AS ( SELECT user_id, MIN(TO_CHAR(created_at, 'YYYY-MM')) AS first_month FROM orders WHERE status != 'cancelled' GROUP BY user_id ) SELECT m.month, COUNT(DISTINCT um.user_id) AS active_users, COUNT(DISTINCT CASE WHEN fo.first_month = m.month THEN fo.user_id END) AS new_users, ( SELECT COUNT(DISTINCT prev.user_id) FROM user_months prev WHERE prev.month = TO_CHAR(TO_DATE(m.month, 'YYYY-MM') - INTERVAL '1 month', 'YYYY-MM') AND prev.user_id NOT IN ( SELECT cur.user_id FROM user_months cur WHERE cur.month = m.month ) ) AS churned_users FROM months m LEFT JOIN user_months um ON um.month = m.month LEFT JOIN first_order fo ON fo.user_id = um.user_id GROUP BY m.month ORDER BY m.month;
|