Analytical and Statistical Queries
Monthly operations summary
SELECT dates.operation_date, COUNT(DISTINCT r.id) as total_reservations, COUNT(DISTINCT o.id) as total_orders, COUNT(DISTINCT r.user_id) as unique_customers, COUNT(DISTINCT fs.employee_id) as active_employees, COALESCE(SUM(oi.quantity * oi.price), 0) as daily_revenue FROM generate_series( CURRENT_DATE - INTERVAL '30 days', CURRENT_DATE, '1 day'::interval ) dates(operation_date) LEFT JOIN reservations r ON DATE(r.datetime) = dates.operation_date LEFT JOIN orders o ON DATE(o.datetime) = dates.operation_date LEFT JOIN order_items oi ON o.id = oi.order_id LEFT JOIN tab_orders to2 ON o.id = to2.order_id LEFT JOIN front_staff fs ON to2.front_staff_id = fs.employee_id GROUP BY dates.operation_date ORDER BY dates.operation_date DESC;
Revenue Split: Online vs. Tab Orders
CREATE OR REPLACE FUNCTION get_revenue_split(p_start_date DATE, p_end_date DATE) RETURNS TABLE(order_type TEXT, total_revenue NUMERIC(14,2)) AS $$ BEGIN RETURN QUERY SELECT 'Online Orders'::text AS order_type, COALESCE(SUM(p.amount), 0)::numeric(14,2) AS total_revenue FROM orders o JOIN payments p ON o.id = p.order_id JOIN online_orders oo ON o.id = oo.order_id WHERE o.datetime::date BETWEEN p_start_date AND p_end_date UNION ALL SELECT 'Tab Orders'::text AS order_type, COALESCE(SUM(p.amount), 0)::numeric(14,2) AS total_revenue FROM orders o JOIN payments p ON o.id = p.order_id JOIN tab_orders tord ON o.id = tord.order_id WHERE o.datetime::date BETWEEN p_start_date AND p_end_date; END; $$ LANGUAGE plpgsql;
Top 10 products by revenue
SELECT p.id as product_id, p.name as product_name, c.name as category_name, SUM(oi.quantity) as total_quantity_sold, SUM(oi.quantity * oi.price) as total_revenue, ROUND(100.0 * SUM(oi.quantity * oi.price) / SUM(SUM(oi.quantity * oi.price)) OVER (), 2) as revenue_share_percent FROM products p JOIN categories c ON p.category_id = c.id JOIN order_items oi ON p.id = oi.product_id JOIN orders o ON o.id = oi.order_id WHERE o.datetime >= CURRENT_DATE - INTERVAL '90 days' GROUP BY p.id, p.name, c.name ORDER BY total_revenue DESC LIMIT 10;
Revenue by shift period
CREATE OR REPLACE VIEW v_revenue_by_shift_period AS WITH distinct_shift_periods AS ( SELECT DISTINCT start_time::time AS start_t, end_time::time AS end_t FROM shifts ) SELECT TO_CHAR(o.datetime, 'YYYY-MM') AS period, dsp.start_t::text || '-' || dsp.end_t::text AS shift_period, SUM(oi.price * oi.quantity) AS total_revenue FROM orders o JOIN order_items oi ON o.id = oi.order_id JOIN distinct_shift_periods dsp ON o.datetime::time >= dsp.start_t AND o.datetime::time < dsp.end_t GROUP BY period, shift_period ORDER BY period DESC, shift_period ASC;
Managers' shifts above monthly average revenue
WITH manager_worked_shifts AS ( SELECT DISTINCT s.id AS shift_id, s.date, s.start_time, s.end_time, a.employee_id AS manager_id FROM assignments a JOIN shifts s ON s.id = a.shift_id JOIN managers m ON m.employee_id = a.employee_id WHERE s.date >= date_trunc('year', CURRENT_DATE)::date AND s.date < (date_trunc('year', CURRENT_DATE) + INTERVAL '1 year')::date ), shift_revenue AS ( SELECT mws.shift_id, date_trunc('month', mws.date)::date AS month_start, mws.date AS shift_date, mws.start_time, mws.end_time, mws.manager_id, COALESCE(SUM(oi.quantity * oi.price), 0)::numeric(14,2) AS shift_revenue FROM manager_worked_shifts mws LEFT JOIN orders o ON o.datetime::date = mws.date AND o.datetime::time >= mws.start_time AND o.datetime::time < mws.end_time LEFT JOIN order_items oi ON oi.order_id = o.id GROUP BY mws.shift_id, month_start, mws.date, mws.start_time, mws.end_time, mws.manager_id ), monthly_avg AS ( SELECT month_start, AVG(shift_revenue)::numeric(14,2) AS avg_revenue_per_shift FROM shift_revenue GROUP BY month_start ) SELECT to_char(sr.month_start, 'YYYY-MM') AS period, sr.shift_id, sr.shift_date, sr.start_time AS shift_start_time, sr.end_time AS shift_end_time, u.email AS manager_email, sr.shift_revenue, ma.avg_revenue_per_shift, (sr.shift_revenue - ma.avg_revenue_per_shift)::numeric(14,2) AS above_by FROM shift_revenue sr JOIN monthly_avg ma ON ma.month_start = sr.month_start JOIN managers m ON m.employee_id = sr.manager_id JOIN employees e ON e.user_id = m.employee_id JOIN users u ON u.id = e.user_id WHERE sr.shift_revenue > ma.avg_revenue_per_shift ORDER BY period DESC, sr.shift_revenue DESC, sr.shift_date DESC;
Monthly revenue vs labor cost
WITH monthly_revenue AS ( SELECT DATE_TRUNC('month', o.datetime) as operation_month, SUM(oi.quantity * oi.price) as revenue FROM orders o JOIN order_items oi ON o.id = oi.order_id GROUP BY DATE_TRUNC('month', o.datetime) ), monthly_labor_cost AS ( SELECT monthly_assignments.operation_month, SUM(e.gross_salary) as labor_cost FROM ( SELECT DISTINCT DATE_TRUNC('month', s.date) as operation_month, a.employee_id FROM shifts s JOIN assignments a ON s.id = a.shift_id ) as monthly_assignments JOIN employees e ON monthly_assignments.employee_id = e.user_id GROUP BY monthly_assignments.operation_month ) SELECT TO_CHAR(COALESCE(mr.operation_month, mlc.operation_month), 'YYYY-MM') as period, ROUND(COALESCE(mr.revenue, 0)::numeric, 2) as total_revenue, ROUND(COALESCE(mlc.labor_cost, 0)::numeric, 2) as total_labor_cost, ROUND( CASE WHEN COALESCE(mr.revenue, 0) > 0 THEN (COALESCE(mlc.labor_cost, 0) / mr.revenue * 100) ELSE 0 END::numeric, 2 ) as labor_as_percent_of_revenue FROM monthly_revenue mr FULL OUTER JOIN monthly_labor_cost mlc ON mr.operation_month = mlc.operation_month ORDER BY period DESC;
Server performance & revenue ranking
WITH server_metrics AS ( SELECT fs.employee_id, u.email as server_email, COUNT(DISTINCT a.id) as total_assignments, COUNT(DISTINCT o.id) as orders_processed, COALESCE(SUM(oi.quantity * oi.price), 0) as total_revenue_generated FROM front_staff fs JOIN employees e ON fs.employee_id = e.user_id JOIN users u ON e.user_id = u.id JOIN staff_roles sr ON fs.staff_role_id = sr.id LEFT JOIN assignments a ON fs.employee_id = a.employee_id LEFT JOIN shifts s ON a.shift_id = s.id LEFT JOIN tab_orders to2 ON to2.front_staff_id = fs.employee_id LEFT JOIN orders o ON o.id = to2.order_id AND o.datetime >= CURRENT_DATE - INTERVAL '3 months' LEFT JOIN order_items oi ON o.id = oi.order_id WHERE LOWER(sr.name) = 'server' GROUP BY fs.employee_id, u.email, u.phone_number, e.net_salary, e.gross_salary, fs.tip_percent, sr.name ), performance_ranking AS ( SELECT *, RANK() OVER (ORDER BY total_revenue_generated DESC) as revenue_rank, RANK() OVER (ORDER BY orders_processed DESC) as orders_rank, CASE WHEN total_assignments > 0 THEN (orders_processed::float / total_assignments) ELSE 0 END as orders_per_assignment, CASE WHEN orders_processed > 0 THEN total_revenue_generated / orders_processed ELSE 0 END as avg_revenue_per_order FROM server_metrics ) SELECT server_email, total_assignments, orders_processed, total_revenue_generated, revenue_rank, orders_rank, ROUND(orders_per_assignment::numeric, 2) as avg_orders_per_shift, ROUND(avg_revenue_per_order::numeric, 2) as avg_order_value FROM performance_ranking ORDER BY total_revenue_generated DESC, orders_processed DESC;
Reservations by Day of Week
CREATE OR REPLACE FUNCTION get_reservations_by_day(p_start_date DATE, p_end_date DATE) RETURNS TABLE(day_of_week INT, total_reservations BIGINT) AS $$ BEGIN RETURN QUERY SELECT EXTRACT(DOW FROM datetime)::INT AS day_of_week, -- 0=Sunday, 1=Monday, etc. COUNT(*) AS total_reservations FROM reservations WHERE DATE(datetime) BETWEEN p_start_date AND p_end_date GROUP BY day_of_week ORDER BY total_reservations DESC; END; $$ LANGUAGE plpgsql;
Average Shift Duration per Manager
SELECT manager_id, AVG(EXTRACT(EPOCH FROM (end_time - start_time)) / 3600) AS avg_shift_duration_hours FROM shifts GROUP BY manager_id;
Yearly Revenue Report
CREATE OR REPLACE FUNCTION get_yearly_revenue(p_start_date DATE, p_end_date DATE) RETURNS TABLE(payment_year DATE, total_revenue NUMERIC) AS $$ BEGIN RETURN QUERY SELECT date_trunc('year', created_at)::DATE AS payment_year,* SUM(amount) AS total_revenue FROM payments WHERE DATE(created_at) BETWEEN p_start_date AND p_end_date GROUP BY date_trunc('year', created_at) ORDER BY payment_year; END; $$ LANGUAGE plpgsql;
Last modified
17 hours ago
Last modified on 09/03/25 18:35:32
Note:
See TracWiki
for help on using the wiki.