Analyst desk with freight performance charts on a laptop and highlighted printed reports under a brass lamp
Guide

Lane Profitability with SQL: Revenue per Mile, Deadhead and Margin

Your TMS already holds the answer to which lanes and customers make money. This guide shows how to define the metrics that matter, avoid the traps that make lane reports lie, and pull the numbers with plain SQL.

Guide12 min readSQL & analyticsExample queries

Why lane profitability hides in plain sight

Ask a carrier which lanes make money and you will usually get a confident answer from dispatch, a different one from sales, and a third from accounting. Each is looking at a different slice: dispatch sees how hard the lane is to cover, sales sees the rate, accounting sees totals by customer at month-end. Nobody is wrong, but nobody has the full picture of revenue, miles, empty miles and cost tied together load by load.

The good news is that most of that data already sits in your TMS database. Order entry captured the rate and the lane. Dispatch captured the loaded and empty miles. Billing captured fuel surcharge and accessorials. Settlements and carrier pay captured the largest direct costs. SQL is simply the tool that joins them up.

This guide is written for the owner, CFO, operations manager or analyst who wants real numbers. You do not need to be a database administrator, but you do need read access to your data and a willingness to define terms precisely.

Define the metrics before you write a query

Most arguments about lane profitability are really arguments about definitions. Agree on these first and write them down.

MetricDefinitionWatch out for
Revenue per loaded mileRevenue divided by loaded milesFlatters lanes that create lots of deadhead
Revenue per total mileRevenue divided by loaded plus empty milesDepends on how you assign empty miles to loads
Linehaul RPMLinehaul revenue only, excluding FSC and accessorialsDo not compare it with all-in RPM
Deadhead %Empty miles divided by total milesMissing empty miles make it look better than it is
Direct marginRevenue minus direct load costs (driver pay or carrier pay, fuel, tolls, load-specific expenses)Excludes fixed overhead; label it as direct
Accessorial captureAccessorials billed versus accessorials earned (detention, layover, extra stops, lumper, TONU)Requires event data such as arrival and departure times

Three decisions to make up front

  • Which miles? Use the same mileage source for every load (for example, practical route miles from your mileage software, or ELD-recorded miles). Mixing billed miles, practical miles and odometer miles in one report quietly distorts every RPM figure.
  • Where does deadhead go? The most common rule charges empty miles driven to reach a pickup to the load being picked up. An alternative charges empty miles after delivery to the load that left the truck there. Pick one.
  • What counts as a lane? State to state is easy but coarse. City pairs or three-digit ZIP pairs are more precise but thin. Many carriers report at market or region level with drill-down to city pairs.

The example schema

Illustrative example only

The tables, columns and queries below use a simplified, hypothetical schema written for this guide. They are not from any specific TMS. Your table and column names will differ, and date functions vary by database. Treat these as patterns to adapt, and test against a reporting copy, not your live production database.

-- Hypothetical example schema (not from any real TMS)
loads(load_id, customer_id, origin_city, origin_state,
      dest_city, dest_state, delivered_date, status,
      linehaul_amt, fsc_amt, accessorial_amt,
      loaded_miles, empty_miles)

customers(customer_id, customer_name)

load_costs(load_id, cost_type, amount)
  -- cost_type examples: 'DRIVER_PAY', 'CARRIER_PAY', 'FUEL', 'TOLLS'

stops(load_id, stop_seq, stop_type, arrived_at, departed_at,
      free_minutes)

accessorial_charges(load_id, charge_code, amount)
  -- charge_code examples: 'DET', 'LAY', 'STOP', 'LUMP', 'TONU'

Query 1: Revenue per loaded mile, per total mile and deadhead by lane

This query rolls delivered loads up to state-to-state lanes for a date range and returns volume, revenue, both RPM figures and deadhead percentage. Multiplying by 1.0 forces decimal math so integer miles do not truncate the result. NULLIF prevents divide-by-zero errors when miles are missing.

-- Example only: adapt names and date syntax to your database
SELECT
    l.origin_state,
    l.dest_state,
    COUNT(*)                                        AS loads,
    SUM(l.linehaul_amt + l.fsc_amt + l.accessorial_amt) AS revenue,
    SUM(l.loaded_miles)                             AS loaded_miles,
    SUM(l.empty_miles)                              AS empty_miles,
    1.0 * SUM(l.linehaul_amt + l.fsc_amt + l.accessorial_amt)
        / NULLIF(SUM(l.loaded_miles), 0)            AS rev_per_loaded_mile,
    1.0 * SUM(l.linehaul_amt + l.fsc_amt + l.accessorial_amt)
        / NULLIF(SUM(l.loaded_miles + l.empty_miles), 0) AS rev_per_total_mile,
    1.0 * SUM(l.linehaul_amt)
        / NULLIF(SUM(l.loaded_miles), 0)            AS linehaul_rpm,
    100.0 * SUM(l.empty_miles)
        / NULLIF(SUM(l.loaded_miles + l.empty_miles), 0) AS deadhead_pct
FROM loads l
WHERE l.status = 'DELIVERED'
  AND l.delivered_date >= '2026-01-01'
  AND l.delivered_date <  '2026-07-01'
GROUP BY l.origin_state, l.dest_state
HAVING COUNT(*) >= 10
ORDER BY rev_per_total_mile DESC;

The HAVING clause hides lanes with fewer than ten loads, which keeps one odd load from topping the list. Adjust the threshold to your volume.

How to read it

  • A lane with strong revenue per loaded mile but weak revenue per total mile is paying well for the loaded leg and then stranding trucks. Look at what happens after delivery.
  • A lane with modest RPM but very low deadhead may be one of your best, because it keeps trucks moving and balances the network.
  • A large gap between linehaul RPM and all-in RPM tells you how much the lane leans on FSC and accessorials.

Query 2: Direct margin by customer

Revenue tells you who is busy. Margin tells you who pays. This query totals direct costs per load first, then joins them to revenue so a load with several cost lines is not counted more than once, a common mistake that inflates revenue.

-- Example only
WITH cost_per_load AS (
    SELECT load_id, SUM(amount) AS direct_cost
    FROM load_costs
    GROUP BY load_id
)
SELECT
    c.customer_name,
    COUNT(*)                                             AS loads,
    SUM(l.linehaul_amt + l.fsc_amt + l.accessorial_amt)  AS revenue,
    SUM(COALESCE(k.direct_cost, 0))                      AS direct_cost,
    SUM(l.linehaul_amt + l.fsc_amt + l.accessorial_amt)
      - SUM(COALESCE(k.direct_cost, 0))                  AS direct_margin,
    100.0 * (SUM(l.linehaul_amt + l.fsc_amt + l.accessorial_amt)
             - SUM(COALESCE(k.direct_cost, 0)))
      / NULLIF(SUM(l.linehaul_amt + l.fsc_amt + l.accessorial_amt), 0)
                                                         AS margin_pct,
    SUM(CASE WHEN k.load_id IS NULL THEN 1 ELSE 0 END)   AS loads_missing_cost
FROM loads l
JOIN customers c        ON c.customer_id = l.customer_id
LEFT JOIN cost_per_load k ON k.load_id = l.load_id
WHERE l.status = 'DELIVERED'
  AND l.delivered_date >= '2026-01-01'
  AND l.delivered_date <  '2026-07-01'
GROUP BY c.customer_name
ORDER BY direct_margin DESC;

The loads_missing_cost column matters. If a customer shows a beautiful margin but half its loads have no cost recorded, the margin is fiction until settlements or carrier pay catch up. Run margin reports only for periods where pay is closed.

For brokerage, the same pattern works with carrier pay as the main cost line, and the result is gross margin per load. For asset fleets, you may also want a fully loaded view that allocates fixed costs such as equipment, insurance and overhead per mile. Keep that as a separate, clearly labeled report so the direct margin stays clean.

Query 3: Accessorial capture, starting with detention

Missed accessorials are revenue you already earned and never billed. Detention is the most common one. If your TMS or ELD integration captures arrival and departure times at each stop, you can find stops where dwell time exceeded free time but no detention charge exists.

-- Example only. DATEDIFF(minute, a, b) is SQL Server style.
-- PostgreSQL equivalent: EXTRACT(EPOCH FROM (b - a)) / 60
WITH dwell AS (
    SELECT
        s.load_id,
        s.stop_seq,
        s.stop_type,
        DATEDIFF(minute, s.arrived_at, s.departed_at) AS dwell_minutes,
        s.free_minutes
    FROM stops s
    WHERE s.arrived_at IS NOT NULL
      AND s.departed_at IS NOT NULL
)
SELECT
    l.load_id,
    c.customer_name,
    d.stop_seq,
    d.stop_type,
    d.dwell_minutes,
    d.free_minutes,
    d.dwell_minutes - d.free_minutes AS billable_minutes
FROM dwell d
JOIN loads l     ON l.load_id = d.load_id
JOIN customers c ON c.customer_id = l.customer_id
LEFT JOIN accessorial_charges a
       ON a.load_id = d.load_id
      AND a.charge_code = 'DET'
WHERE d.dwell_minutes > d.free_minutes
  AND a.load_id IS NULL
  AND l.delivered_date >= '2026-01-01'
ORDER BY billable_minutes DESC;

Each row is a candidate, not an automatic invoice. Some customers have contracts that waive detention, some stops are the carrier's fault, and some arrival times are wrong. But the list gives billing a short, specific worklist instead of a vague sense that detention is slipping. The same pattern works for extra stops (stop count versus stop charges), layovers and lumper receipts without a matching charge.

Query 4: Data quality checks to run first

Before anyone makes pricing decisions from these numbers, check the inputs. A five-minute data quality query saves a lot of embarrassing meetings.

-- Example only: count suspicious delivered loads by issue
SELECT 'Zero or missing loaded miles' AS issue, COUNT(*) AS loads
FROM loads
WHERE status = 'DELIVERED' AND COALESCE(loaded_miles, 0) = 0
UNION ALL
SELECT 'Zero linehaul revenue', COUNT(*)
FROM loads
WHERE status = 'DELIVERED' AND COALESCE(linehaul_amt, 0) = 0
UNION ALL
SELECT 'Missing empty miles (NULL)', COUNT(*)
FROM loads
WHERE status = 'DELIVERED' AND empty_miles IS NULL
UNION ALL
SELECT 'No direct cost recorded', COUNT(*)
FROM loads l
WHERE l.status = 'DELIVERED'
  AND NOT EXISTS (SELECT 1 FROM load_costs k WHERE k.load_id = l.load_id);

Data quality checklist

  • Duplicate customers merged, so one account is not split across several names.
  • One consistent mileage source for loaded and empty miles.
  • Rebills, credits and cancelled loads handled consistently (excluded, or netted against the original load).
  • Accessorials billed on supplemental invoices tied back to the original load ID.
  • Costs posted at the load level, not only as lump sums in accounting.
  • Driver pay and carrier pay closed for the period you are reporting.

Turning numbers into decisions

A lane report earns its keep when it changes what people do on Monday morning. Common uses:

  • Pricing and bid season. Bring actual revenue per total mile and deadhead by lane into rate reviews and RFP responses instead of relying on memory.
  • Network balance. Pair lanes that strand trucks with backhaul freight, or price the deadhead into the headhaul rate.
  • Customer conversations. Use dwell and detention data to discuss facility performance with shippers, with facts in hand.
  • Billing discipline. Turn the accessorial capture query into a daily or weekly exception report for billing.
  • Sales targeting. Focus sales effort on freight that fills weak lanes, not just more freight.

Schedule the queries, put the results in a simple dashboard or report, and review them on a fixed rhythm. A monthly lane review with operations, sales and finance in the same room does more than any one-off analysis.

Carrier Lane Profitability & Network Contribution Matrix

A lane cannot be evaluated in isolation. A high-rate load into a dead market can cost more in repositioning deadhead than a low-rate load on a balanced corridor. Use this strategic contribution matrix to classify your freight network:

Lane Classification Revenue & Deadhead Profile Fully-Allocated Target Margin Strategic Fleet Operating Action
Core Premium Headhaul High RPM (\$3.20-\$4.50/mi), loaded ratio >92%, predictable shipper appointment windows. 22% to 35% net operating margin. Protect fleet capacity; negotiate multi-year contract caps; prioritize for highest-performing company drivers.
Balanced Two-Way Corridor Moderate RPM (\$2.40-\$2.80/mi) outbound and inbound; total round-trip deadhead <10%. 15% to 22% net operating margin. Continuous loop optimization; assign dedicated slip-seat power units to maximize weekly asset utilization.
Repositioning Backhaul Lower RPM (\$1.65-\$2.10/mi) covering marginal cash operating costs into high-demand freight origins. 2% to 8% marginal cash contribution. Strict rule: must cover direct fuel and driver pay; cap total miles; avoid shippers with dwell >90 minutes.
Chronic Margin Bleed Low rate, unbilled detention, empty deadhead >120 miles to reload, shipper payment terms >45 days. Negative net margin (-5% to -20%). Issue immediate 18% rate increase or accessorial addendum; if customer refuses, exit lane within 30 days.

SQL Query Architecture & Multi-Stop Round-Trip Yield Matrix

Evaluating single load legs in isolation hides true network profitability. Forensic lane analysis requires grouping consecutive load legs into continuous power-unit circuits to calculate real round-trip contribution:

Circuit Type Dispatch Leg Routing SQL CTE Aggregation Logic Cost & Revenue Drivers Net Round-Trip Evaluation Metric
Direct Two-Way Corridor Chicago, IL to Dallas, TX (Headhaul) → Dallas, TX to Chicago, IL (Direct Backhaul) Join outbound leg (Seq 1) and return leg (Seq 2) by power unit ID within 96-hour window. Gross linehaul + fuel surcharge minus combined driver pay, road fuel card debits, and turnpike tolls. Target: > $2.55 / Total Round-Trip Mile with < 6.5% total circuit deadhead.
Triangular Repositioning Loop Atlanta, GA to Miami, FL (Headhaul) → Miami to Jacksonville, FL (Empty Reposition) → Jacksonville to Atlanta (Backhaul) Window functions (LEAD / LAG) capturing unbilled transit miles between delivery and subsequent pickup. Charge 100% of Miami-to-Jacksonville empty diesel and driver hourly pay against Miami inbound freight margin. Target: Outbound Atlanta rate must absorb Florida backhaul deficit ($2.80 combined loop average).
Multi-Stop Peddle Circuit Terminal Hub to Stop 1 (Drop) → Stop 2 (Drop) → Stop 3 (Final Drop & Sweep) Aggregate total stop accessorial fees (SUM(charge_amt) WHERE charge_code IN ('STOP','DET')) across parent trip ID. Compare stop pay compensation paid to city cartage driver against customer tariff stop-off schedule. Target: Zero unrecovered stop-off charges; minimum $950 / Seated Day revenue velocity.
Dedicated Captive Shuttle Manufacturing Plant to Regional Distribution Center (Continuous loop, 4 turns/day) Group by customer account ID and dedicated contract billing rate per round-trip turn. Fixed driver hourly pay and dedicated tractor lease allocation divided by total circuit cycles. Target: Operating Ratio < 88.5%; contractual deadhead reimbursement for unladen repositioning.
Spot Market Rescue Run Contract Delivery to Remote Destination → Broker Spot Load to Nearest Core Freight Corridor Join TMS order billing table to external broker rate confirmation AP voucher. Factor broker quick-pay fees (2% to 4%) and unapproved detention write-offs into true net settlement cash. Target: Cash contribution must exceed direct fuel and driver variable compensation ($1.85/mi floor).

How Strong Tower Consulting helps

Strong Tower has been mining carrier databases since 2008. We map where your TMS actually stores revenue, miles, stops and costs, clean up the data that skews results, write and validate the queries, and build the recurring reports your team will use. We work with the system you already have.

Independent by design

Strong Tower Consulting never requires you to buy our software. We analyze your company through the technology you already run and help you get more out of it. Read more about how we work, or see how billing data flows in our order-to-cash guide.

Frequently asked questions

Should revenue per mile include fuel surcharge?

Report both. Linehaul-only revenue per mile shows the strength of the underlying rate, while all-in revenue per mile, including FSC and accessorials, shows what the truck actually earned. The key is to label each clearly and never compare one against the other.

How should deadhead be assigned to a lane?

Pick one rule and apply it consistently. The most common is to charge the empty miles driven to reach a pickup to the load being picked up. Some carriers instead charge the empty miles after delivery to the load that left the truck there, which better reflects lanes that strand equipment in weak markets.

Can we run these queries directly against our TMS database?

Often, yes, but run heavy queries against a reporting copy or replica rather than the live production database during business hours, and confirm your vendor agreement allows direct database access. Table and column names will differ from the examples in this guide.

What is the most common reason lane reports are wrong?

Inconsistent source data: missing or estimated miles, accessorials billed on separate invoices that are not tied back to the load, costs posted only in accounting, and duplicate customer records that split one account into several.

How do you calculate fully-allocated tractor operating costs per lane?

We combine direct variable expenses with fixed overhead allocations. Variable costs (fuel spend from fuel card imports, driver mileage/stop pay, tires, and maintenance per mile) are allocated directly to the trip. Fixed overhead (tractor/trailer lease or depreciation, physical damage insurance, licenses, and terminal administrative overhead) is divided by average weekly operating miles or days in transit. When aggregated via SQL, this gives owners the true fully-allocated operating cost per mile to measure against invoiced freight revenue.

How does our sister company Exspeedite track real-time lane margin inside TMS dispatch?

Exspeedite - a Strong Tower company operating as a sister company under common ownership - features native lane rate intelligence and RFQ multi-lane quoting tools directly embedded within its dispatch HUD. When dispatchers price or book a load, the system automatically correlates historical lane yields, DOE diesel fuel index benchmarks, and practical route miles to project net trip margin before the load is ever tendered to a driver, preventing accidental unprofitable dispatch.

SQL Server is a trademark of Microsoft Corporation. PostgreSQL is a trademark of the PostgreSQL Community Association of Canada. All trademarks belong to their respective holders. Strong Tower Consulting is independent and not affiliated with or endorsed by these companies.

Start Here

Find out which lanes really pay.

We turn the data already sitting in your TMS into lane, customer and accessorial reports you can act on.