Recursive CTE — Building a Shipment Event Timeline

Recursive CTE — Building a Shipment Event Timeline

Delivery events are an ordered chain: picked → in_transit → out_for_delivery → delivered. A recursive CTE can traverse this chain to compute time between milestones and identify where shipments spend the most time.

-- Pivot delivery milestones into one row per shipment
WITH milestones AS (
    SELECT
        shipment_id,
        MAX(occurred_at) FILTER (WHERE status = 'picked')           AS picked_at,
        MAX(occurred_at) FILTER (WHERE status = 'in_transit')       AS in_transit_at,
        MAX(occurred_at) FILTER (WHERE status = 'out_for_delivery') AS out_for_delivery_at,
        MAX(occurred_at) FILTER (WHERE status = 'delivered')        AS delivered_at_event
    FROM lg_delivery_events
    GROUP BY shipment_id
)
SELECT
    s.order_ref,
    s.carrier,
    s.service_level,
    ROUND(EXTRACT(EPOCH FROM (m.in_transit_at - m.picked_at)) / 3600, 1)         AS pickup_to_transit_hrs,
    ROUND(EXTRACT(EPOCH FROM (m.out_for_delivery_at - m.in_transit_at)) / 3600, 1) AS transit_to_ofd_hrs,
    ROUND(EXTRACT(EPOCH FROM (m.delivered_at_event - m.out_for_delivery_at)) / 3600, 1) AS ofd_to_delivery_hrs,
    ROUND(EXTRACT(EPOCH FROM (m.delivered_at_event - m.picked_at)) / 3600, 1)    AS total_hrs
FROM milestones m
JOIN lg_shipments s ON s.id = m.shipment_id
WHERE m.picked_at IS NOT NULL AND m.delivered_at_event IS NOT NULL
ORDER BY total_hrs DESC
LIMIT 20;

Purchase this course to unlock the full lesson.

Sign up