GuideApril 23, 2026·By Crew

How We Calculate Revenue Per Visit — The Technical Breakdown

Technical companion to Revenue Per Visit in The Numbers That Matter series.


Revenue Per Visit sounds like simple division. In practice, getting a reliable RPV requires every dollar tied to the right visit, and a data model that makes slicing trivial.

The formula

RPV = Total Revenue from Visits / Total Completed Visits

Simple — but "Total Revenue from Visits" is doing a lot of work. It means every charge generated during a visit needs to be associated with that visit record: admission, rental fees, add-on purchases, overage charges, late fees, everything.

The data model that makes it work

The key relationship is: every transaction belongs to a visit. Not to a member, not to a shift, not to a day — to a visit.

Visit
  ├── admission_charge
  ├── Rental(s)
  │     └── rental_charge (hourly × duration, or flat rate)
  ├── Purchase(s)
  │     └── product, quantity, unit_price, total
  └── Fee(s)
        └── fee_type, amount (e.g., lost bracelet fee)

With this structure, RPV for any visit is:

SELECT
  v.id,
  COALESCE(v.admission_charge, 0)
  + COALESCE(SUM(r.rental_charge), 0)
  + COALESCE(SUM(p.total), 0)
  + COALESCE(SUM(f.amount), 0) AS visit_revenue
FROM visits v
LEFT JOIN rentals r ON r.visit_id = v.id
LEFT JOIN purchases p ON p.visit_id = v.id
LEFT JOIN fees f ON f.visit_id = v.id
WHERE v.status = 'completed'
GROUP BY v.id;

And aggregate RPV is just:

SELECT AVG(visit_revenue) AS rpv FROM (above query);

Slicing RPV

The power of RPV is in the slices. Because the visit record connects to everything, you can cut by any dimension:

By day of week:

WHERE EXTRACT(DOW FROM v.checked_in_at) = 6  -- Saturday

By membership tier:

JOIN members m ON v.member_id = m.id
JOIN membership_tiers t ON m.tier_id = t.id
WHERE t.name = 'VIP'

By time of day:

WHERE EXTRACT(HOUR FROM v.checked_in_at) BETWEEN 18 AND 23

By date range (for before/after comparisons):

WHERE v.checked_in_at BETWEEN '2026-01-01' AND '2026-01-31'

Each slice is a WHERE clause or a GROUP BY — no restructuring needed.

Denormalization for performance

If you're querying RPV frequently (e.g., showing it on a Manager dashboard in real time), you can denormalize a total_revenue field directly onto the visit record. Update it on every transaction that touches the visit. This trades a tiny write cost for zero-join reads:

Visit
  total_revenue: Decimal  -- updated on every charge event

RPV then becomes:

SELECT AVG(total_revenue) FROM visits
WHERE status = 'completed'
  AND checked_in_at >= NOW() - INTERVAL '30 days';

One table, one index, instant result.

Handling multi-member visits

In a shared room scenario (two members in one rental), the rental charge can be split or attributed to the primary visit. The approach depends on the club's preference, but the data model should support both:

Option A — Full attribution: the rental charge belongs to the visit that booked the room. Other members' visits only include their own purchases and fees. RPV reflects total visit economics for the booking member.

Option B — Split attribution: the rental charge is divided among all visit records associated with the rental. RPV reflects per-person economics. This is fairer for analysis but slightly more complex.

Either works as long as it's consistent. The key is that the attribution logic is defined once and applied uniformly.

What "good" RPV analysis looks like

The most useful RPV analysis isn't a single number — it's a table:

Segment              | Avg RPV | Visit Count | Total Revenue
---------------------|---------|-------------|---------------
Saturday Night       | $67.40  | 482         | $32,486
Weekday Evening      | $41.20  | 1,203       | $49,563
Weekday Afternoon    | $28.90  | 387         | $11,184
VIP Members          | $72.10  | 341         | $24,586
Default Members      | $38.50  | 1,731       | $66,643

This is straightforward aggregation with the visit-centric data model. No ETL pipeline, no data warehouse — just SQL against your operational database.


Back to the main article: Revenue Per Visit — The Number That Tells You If Your Business Model Is Working