Changes between Version 5 and Version 6 of AnalyticalStatisticalQuerying


Ignore:
Timestamp:
09/03/25 18:35:32 (20 hours ago)
Author:
226030
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • AnalyticalStatisticalQuerying

    v5 v6  
    11== Analytical and Statistical Queries
    22
    3 **Daily Revenue**
    4 {{{
    5 CREATE OR REPLACE FUNCTION get_daily_revenue(p_start_date DATE, p_end_date DATE)
    6 RETURNS TABLE(payment_date DATE, total_revenue NUMERIC) AS $$
     3**Monthly operations summary**
     4{{{
     5SELECT
     6    dates.operation_date,
     7    COUNT(DISTINCT r.id) as total_reservations,
     8    COUNT(DISTINCT o.id) as total_orders,
     9    COUNT(DISTINCT r.user_id) as unique_customers,
     10    COUNT(DISTINCT fs.employee_id) as active_employees,
     11    COALESCE(SUM(oi.quantity * oi.price), 0) as daily_revenue
     12FROM generate_series(
     13                         CURRENT_DATE - INTERVAL '30 days',
     14                         CURRENT_DATE,
     15                         '1 day'::interval
     16         ) dates(operation_date)
     17         LEFT JOIN reservations r
     18                   ON DATE(r.datetime) = dates.operation_date
     19    LEFT JOIN orders o
     20ON DATE(o.datetime) = dates.operation_date
     21    LEFT JOIN order_items oi
     22    ON o.id = oi.order_id
     23    LEFT JOIN tab_orders to2
     24    ON o.id = to2.order_id
     25    LEFT JOIN front_staff fs
     26    ON to2.front_staff_id = fs.employee_id
     27GROUP BY dates.operation_date
     28ORDER BY dates.operation_date DESC;
     29}}}
     30
     31** Revenue Split: Online vs. Tab Orders **
     32{{{
     33CREATE OR REPLACE FUNCTION get_revenue_split(p_start_date DATE, p_end_date DATE)
     34RETURNS TABLE(order_type TEXT, total_revenue NUMERIC(14,2)) AS $$
    735BEGIN
    8     RETURN QUERY
    9     SELECT
    10         DATE(created_at) AS payment_date,
    11         SUM(amount) AS total_revenue
    12     FROM payments
    13     WHERE DATE(created_at) BETWEEN p_start_date AND p_end_date
    14     GROUP BY DATE(created_at)
    15     ORDER BY payment_date;
     36RETURN QUERY
     37SELECT 'Online Orders'::text AS order_type,
     38        COALESCE(SUM(p.amount), 0)::numeric(14,2) AS total_revenue
     39FROM orders o
     40         JOIN payments p ON o.id = p.order_id
     41         JOIN online_orders oo ON o.id = oo.order_id
     42WHERE o.datetime::date BETWEEN p_start_date AND p_end_date
     43
     44UNION ALL
     45
     46SELECT 'Tab Orders'::text AS order_type,
     47        COALESCE(SUM(p.amount), 0)::numeric(14,2) AS total_revenue
     48FROM orders o
     49         JOIN payments p ON o.id = p.order_id
     50         JOIN tab_orders tord ON o.id = tord.order_id
     51WHERE o.datetime::date BETWEEN p_start_date AND p_end_date;
    1652END;
    1753$$ LANGUAGE plpgsql;
    18 
    19 SELECT * FROM get_daily_revenue('2025-01-01', '2025-01-31');
    20 }}}
    21 
    22 **Daily Order Count and Average Order Value Report**
    23 {{{
    24 CREATE OR REPLACE FUNCTION get_daily_order_stats(p_start_date DATE, p_end_date DATE)
    25 RETURNS TABLE(order_date DATE, total_orders BIGINT, avg_order_value NUMERIC) AS $$
    26 BEGIN
    27     RETURN QUERY
    28     SELECT
    29         DATE(o.datetime) AS order_date,
    30         COUNT(o.id) AS total_orders,
    31         AVG(p.amount) AS avg_order_value
     54}}}
     55
     56**Top 10 products by revenue **
     57{{{
     58SELECT
     59    p.id as product_id,
     60    p.name as product_name,
     61    c.name as category_name,
     62    SUM(oi.quantity) as total_quantity_sold,
     63    SUM(oi.quantity * oi.price) as total_revenue,
     64    ROUND(100.0 * SUM(oi.quantity * oi.price) / SUM(SUM(oi.quantity * oi.price)) OVER (), 2) as revenue_share_percent
     65FROM products p
     66         JOIN categories c ON p.category_id = c.id
     67         JOIN order_items oi ON p.id = oi.product_id
     68         JOIN orders o ON o.id = oi.order_id
     69WHERE o.datetime >= CURRENT_DATE - INTERVAL '90 days'
     70GROUP BY p.id, p.name, c.name
     71ORDER BY total_revenue DESC
     72    LIMIT 10;
     73}}}
     74
     75**Revenue by shift period**
     76{{{
     77CREATE OR REPLACE VIEW v_revenue_by_shift_period AS
     78WITH distinct_shift_periods AS (
     79    SELECT DISTINCT
     80        start_time::time AS start_t,
     81        end_time::time AS end_t
     82    FROM
     83        shifts
     84)
     85SELECT
     86    TO_CHAR(o.datetime, 'YYYY-MM') AS period,
     87    dsp.start_t::text || '-' || dsp.end_t::text AS shift_period,
     88    SUM(oi.price * oi.quantity) AS total_revenue
     89FROM
     90    orders o
     91        JOIN
     92    order_items oi ON o.id = oi.order_id
     93        JOIN
     94    distinct_shift_periods dsp ON o.datetime::time >= dsp.start_t AND o.datetime::time < dsp.end_t
     95GROUP BY
     96    period,
     97    shift_period
     98ORDER BY
     99    period DESC,
     100    shift_period ASC;
     101}}}
     102
     103** Managers' shifts above monthly average revenue **
     104{{{
     105WITH manager_worked_shifts AS (
     106    SELECT DISTINCT
     107        s.id AS shift_id,
     108        s.date,
     109        s.start_time,
     110        s.end_time,
     111        a.employee_id AS manager_id
     112    FROM assignments a
     113             JOIN shifts s   ON s.id = a.shift_id
     114             JOIN managers m ON m.employee_id = a.employee_id
     115    WHERE s.date >= date_trunc('year', CURRENT_DATE)::date
     116    AND s.date <  (date_trunc('year', CURRENT_DATE) + INTERVAL '1 year')::date
     117    ),
     118    shift_revenue AS (
     119SELECT
     120    mws.shift_id,
     121    date_trunc('month', mws.date)::date AS month_start,
     122    mws.date AS shift_date,
     123    mws.start_time,
     124    mws.end_time,
     125    mws.manager_id,
     126    COALESCE(SUM(oi.quantity * oi.price), 0)::numeric(14,2) AS shift_revenue
     127FROM manager_worked_shifts mws
     128    LEFT JOIN orders o
     129ON o.datetime::date = mws.date
     130    AND o.datetime::time >= mws.start_time
     131    AND o.datetime::time <  mws.end_time
     132    LEFT JOIN order_items oi ON oi.order_id = o.id
     133GROUP BY
     134    mws.shift_id, month_start, mws.date, mws.start_time, mws.end_time, mws.manager_id
     135    ),
     136    monthly_avg AS (
     137SELECT month_start, AVG(shift_revenue)::numeric(14,2) AS avg_revenue_per_shift
     138FROM shift_revenue
     139GROUP BY month_start
     140    )
     141SELECT
     142    to_char(sr.month_start, 'YYYY-MM') AS period,
     143    sr.shift_id,
     144    sr.shift_date,
     145    sr.start_time AS shift_start_time,
     146    sr.end_time   AS shift_end_time,
     147    u.email       AS manager_email,
     148    sr.shift_revenue,
     149    ma.avg_revenue_per_shift,
     150    (sr.shift_revenue - ma.avg_revenue_per_shift)::numeric(14,2) AS above_by
     151FROM shift_revenue sr
     152         JOIN monthly_avg ma ON ma.month_start = sr.month_start
     153         JOIN managers m     ON m.employee_id = sr.manager_id
     154         JOIN employees e    ON e.user_id     = m.employee_id
     155         JOIN users u        ON u.id          = e.user_id
     156WHERE sr.shift_revenue > ma.avg_revenue_per_shift
     157ORDER BY period DESC, sr.shift_revenue DESC, sr.shift_date DESC;
     158}}}
     159
     160** Monthly revenue vs labor cost **
     161{{{
     162WITH monthly_revenue AS (
     163    SELECT
     164        DATE_TRUNC('month', o.datetime) as operation_month,
     165        SUM(oi.quantity * oi.price) as revenue
    32166    FROM orders o
    33     JOIN payments p ON o.id = p.order_id
    34     WHERE DATE(o.datetime) BETWEEN p_start_date AND p_end_date
    35     GROUP BY DATE(o.datetime)
    36     ORDER BY order_date;
    37 END;
    38 $$ LANGUAGE plpgsql;
    39 
    40 SELECT * FROM get_daily_order_stats('2025-01-01', '2025-01-31');
    41 
    42 }}}
    43 
    44 ** Revenue Split: Online vs. Tab Orders **
    45 {{{
    46 CREATE OR REPLACE FUNCTION get_revenue_split(p_start_date DATE, p_end_date DATE)
    47 RETURNS TABLE(order_type TEXT, total_revenue NUMERIC) AS $$
    48 BEGIN
    49     RETURN QUERY
    50     SELECT 'Online Orders' AS order_type, SUM(p.amount) AS total_revenue
    51     FROM orders o
    52     JOIN payments p ON o.id = p.order_id
    53     JOIN online_orders oo ON o.id = oo.order_id
    54     WHERE DATE(o.datetime) BETWEEN p_start_date AND p_end_date
    55 
    56     UNION ALL
    57 
    58     SELECT 'Tab Orders' AS order_type, SUM(p.amount) AS total_revenue
    59     FROM orders o
    60     JOIN payments p ON o.id = p.order_id
    61     JOIN tab_orders tord ON o.id = tord.order_id
    62     WHERE DATE(o.datetime) BETWEEN p_start_date AND p_end_date;
    63 END;
    64 $$ LANGUAGE plpgsql;
    65 
    66 SELECT * FROM get_revenue_split('2025-01-01', '2025-01-28');
    67 }}}
    68 
    69 **Top 10 Best-Selling Products**
    70 {{{
    71 SELECT
    72     p.name AS product,
    73     SUM(oi.quantity) AS total_quantity_sold
    74 FROM order_items oi
    75 JOIN products p ON oi.product_id = p.id
    76 GROUP BY p.name
    77 ORDER BY total_quantity_sold DESC
    78 LIMIT 10;
     167             JOIN order_items oi ON o.id = oi.order_id
     168    GROUP BY DATE_TRUNC('month', o.datetime)
     169),
     170     monthly_labor_cost AS (
     171         SELECT
     172             monthly_assignments.operation_month,
     173             SUM(e.gross_salary) as labor_cost
     174         FROM (
     175                  SELECT DISTINCT
     176                      DATE_TRUNC('month', s.date) as operation_month,
     177                      a.employee_id
     178                  FROM shifts s
     179                           JOIN assignments a ON s.id = a.shift_id
     180              ) as monthly_assignments
     181                  JOIN employees e ON monthly_assignments.employee_id = e.user_id
     182         GROUP BY monthly_assignments.operation_month
     183     )
     184SELECT
     185    TO_CHAR(COALESCE(mr.operation_month, mlc.operation_month), 'YYYY-MM') as period,
     186    ROUND(COALESCE(mr.revenue, 0)::numeric, 2) as total_revenue,
     187    ROUND(COALESCE(mlc.labor_cost, 0)::numeric, 2) as total_labor_cost,
     188    ROUND(
     189            CASE
     190                WHEN COALESCE(mr.revenue, 0) > 0
     191                    THEN (COALESCE(mlc.labor_cost, 0) / mr.revenue * 100)
     192                ELSE 0
     193                END::numeric, 2
     194        ) as labor_as_percent_of_revenue
     195FROM monthly_revenue mr
     196         FULL OUTER JOIN monthly_labor_cost mlc ON mr.operation_month = mlc.operation_month
     197ORDER BY period DESC;
     198}}}
     199
     200** Server performance & revenue ranking **
     201{{{
     202WITH server_metrics AS (
     203    SELECT
     204        fs.employee_id,
     205        u.email as server_email,
     206        COUNT(DISTINCT a.id) as total_assignments,
     207        COUNT(DISTINCT o.id) as orders_processed,
     208        COALESCE(SUM(oi.quantity * oi.price), 0) as total_revenue_generated
     209    FROM front_staff fs
     210             JOIN employees e ON fs.employee_id = e.user_id
     211             JOIN users u ON e.user_id = u.id
     212             JOIN staff_roles sr ON fs.staff_role_id = sr.id
     213             LEFT JOIN assignments a ON fs.employee_id = a.employee_id
     214             LEFT JOIN shifts s ON a.shift_id = s.id
     215             LEFT JOIN tab_orders to2 ON to2.front_staff_id = fs.employee_id
     216             LEFT JOIN orders o ON o.id = to2.order_id
     217        AND o.datetime >= CURRENT_DATE - INTERVAL '3 months'
     218    LEFT JOIN order_items oi ON o.id = oi.order_id
     219WHERE LOWER(sr.name) = 'server'
     220GROUP BY fs.employee_id, u.email, u.phone_number,
     221    e.net_salary, e.gross_salary, fs.tip_percent, sr.name
     222    ),
     223    performance_ranking AS (
     224SELECT *,
     225    RANK() OVER (ORDER BY total_revenue_generated DESC) as revenue_rank,
     226    RANK() OVER (ORDER BY orders_processed DESC) as orders_rank,
     227    CASE
     228    WHEN total_assignments > 0
     229    THEN (orders_processed::float / total_assignments)
     230    ELSE 0
     231    END as orders_per_assignment,
     232    CASE
     233    WHEN orders_processed > 0
     234    THEN total_revenue_generated / orders_processed
     235    ELSE 0
     236    END as avg_revenue_per_order
     237FROM server_metrics
     238    )
     239SELECT
     240    server_email,
     241    total_assignments,
     242    orders_processed,
     243    total_revenue_generated,
     244    revenue_rank,
     245    orders_rank,
     246    ROUND(orders_per_assignment::numeric, 2) as avg_orders_per_shift,
     247    ROUND(avg_revenue_per_order::numeric, 2) as avg_order_value
     248FROM performance_ranking
     249ORDER BY total_revenue_generated DESC, orders_processed DESC;
    79250}}}
    80251