Matchi Data Share Documentation
What is a Data Share?
A data share is a direct connection to a (Snowflake) database providing read-only access to data from your venue. This connection can be used with any BI tool (like Power BI, Looker, Excel) or programmatic tool (like Python, R) that supports Snowflake connections.
The data is updated daily, typically by early morning, and contains all historical data up to the previous day. This means you'll have access to complete data from yesterday and earlier, but not today's data. All data is filtered so that each data share account can only access rows related to its own facilities.
Note: While data is updated daily, customer support and service-level guarantees are only provided on workdays (Monday-Friday), excluding holidays.
In this documentation, "venue" and "facility" are used interchangeably.
How to Connect
When you receive your data share account, you'll get an email with your specific connection details including:
- Snowflake URL: Your unique Snowflake account URL
- Username: Your reader account username
- Password: Initial password (you'll be prompted to change this on first login)
Using Snowflake Web Interface
- Navigate to your provided Snowflake URL
- Log in with your username and password
- You'll be prompted to change your password on first login
- Multi-factor authentication setup is optional and can be added later
- Find your data under: Data > Databases > Matchi > External > Views
- Use the Projects tab to write and execute SQL queries
Connection Parameters for External Tools
Use these parameters when connecting from BI tools, Excel, Python, R, or other applications:
- Server/Host: Your Snowflake account URL
- Database:
matchi - Schema:
external - User: Your provided username
- Authentication: Username/Password
Popular Integrations
- Excel: Install ODBC driver and configure for Windows
- Power BI: Connecting Snowflake with PowerBI
- Python: Use the
snowflake-connector-pythonpackage.
For questions or support, contact team-data@matchi.com.
Data Structure
Fact tables contain measurable business events and transactions. They are used to define metrics like revenue, number of bookings, or membership counts. Each row represents a specific event (like a booking or payment), which is why they typically have multiple rows per entity (e.g., multiple bookings per customer). Both fact and dimension tables contain the full available history — there is no time limit on the data share.
All fact tables have a many-to-one relationship with the facility dimension. This means that each row in a dimension table belongs to exactly one facility, while a facility can have multiple rows in each dimension table.
Similarly, the customer dimension has a one-to-many relationship with fact tables. This means that a single customer can have multiple rows in fact tables (like bookings, revenue, or memberships), while each row in a fact table belongs to exactly one customer.
To enrich your analysis, you can join fact tables to dimension tables using the following keys:
facility_idcustomer_idbooking_idcourt_idactivity_occasion_id
Use these IDs to combine tables and add extra dimensions as needed. Each model has a latest_sync field that shows the last time the table was updated.
Table of Contents
- What is a Data Share?
- How to Connect
- Data Structure
- Examples
- Suggested Metrics
- Tables
Suggested Metrics
The following definitions are used by Matchi. Use them as a starting point to create your own metrics.
| Table | Metric | Definition |
|---|---|---|
| customer_v1 | Active customers | count(customer_id) where archived = false and deleted is null |
| booking_v1 | Total number of bookings | count(distinct booking_id) |
| booking_v1 | Total played hours | sum(duration)/60 |
| customer_participation_log_v1 | Participating customers | count(distinct customer_id) |
| customer_participation_log_v1 | Participations | count(*) |
| facility_home_vs_visitors_v1 | Bookings and activities by player relation | sum(bookings_and_activities) group by player_relation |
| facility_home_vs_visitors_v1 | Money spent by player relation | sum(money_spent) group by player_relation |
| membership_history_v1 | Active members on date | count(distinct customer_id) where state = 'Member' |
| membership_v1 | Total number of memberships | count(distinct membership_id) |
| monthly_coupon_value_v1 | Total gift card value | sum(remaining_amount) |
| monthly_coupon_value_v1 | Total remaining tickets | sum(remaining_nr_of_tickets) |
| revenue_v1 | Total revenue | sum(revenue) |
| schedule_v1 | Occupancy | sum(played_hours) / sum(bookable_hours) |
| statement_data_v1 | Net position | sum(invoicedexvat) group by type |
Examples
Here's an SQL example of how to analyze revenue by facility:
-- Calculate monthly revenue by facility
SELECT
f.facility_name,
DATE_TRUNC('month', r.invoice_date) as month,
SUM(r.revenue) as total_revenue,
COUNT(DISTINCT r.booking_id) as number_of_bookings
FROM revenue_v1 r
LEFT JOIN facility_v1 f ON r.facility_id = f.facility_id
WHERE r.invoice_date >= DATEADD('month', -12, CURRENT_DATE())
GROUP BY facility_name, month
ORDER BY facility_name, month
This query:
- Joins the revenue fact table with the facility dimension
- Groups revenue by facility, and month
- Calculates both total revenue and number of bookings
- Shows the last 12 months of data
- Uses the suggested metric for revenue calculation
Tables
Below are detailed descriptions of each table in the data share, showing their columns and relationships.
Dimension Tables
Dimension tables contain descriptive attributes and are used to categorize, filter, and group fact tables. They typically have one row per entity.
facility_v1
Facility properties.
Changelog:
- 2025-10-03 Added H3 geospatial index columns (levels 3-9) for location-based analysis
📋 Table Structure
| Column | Type | Description |
|---|---|---|
active |
BOOLEAN | Whether the facility is currently searchable in matchi. |
city |
TEXT | The city where the facility is located. |
country |
TEXT | Country where the facility is located. |
currency |
TEXT | The currency the facility trades in (SEK, NOK, DKK, EUR, GBP, ...). |
date_created |
DATE | The date and time the facility was created. |
facility_group_name |
TEXT | The name of the facility group (chain) that the facility belongs to. NULL if the facility is not part of a group. |
facility_id |
INTEGER | (PK) The unique identifier for the facility. |
facility_name |
TEXT | The full name of the facility. |
facility_shortname |
TEXT | The short name of the facility. |
latitude |
FLOAT | Latitude of the facility's location, in decimal degrees (WGS84). |
longitude |
FLOAT | Longitude of the facility's location, in decimal degrees (WGS84). |
municipality |
TEXT | The municipality where the facility is located. |
region |
TEXT | The region (county or equivalent administrative subdivision) where the facility is located. NULL when not known. |
h3_level_9 |
TEXT | H3 geospatial index at resolution 9 (~0.1 km² hexagon). Most granular level for precise location analysis. |
h3_level_8 |
TEXT | H3 geospatial index at resolution 8 (~0.7 km² hexagon). Useful for neighborhood-level analysis. |
h3_level_7 |
TEXT | H3 geospatial index at resolution 7 (~5 km² hexagon). Suitable for district-level analysis. |
h3_level_6 |
TEXT | H3 geospatial index at resolution 6 (~36 km² hexagon). Good for city-level analysis. |
h3_level_5 |
TEXT | H3 geospatial index at resolution 5 (~252 km² hexagon). Regional-level grouping. |
h3_level_4 |
TEXT | H3 geospatial index at resolution 4 (~1,770 km² hexagon). Large regional areas. |
h3_level_3 |
TEXT | H3 geospatial index at resolution 3 (~12,392 km² hexagon). Country-level grouping. |
latest_sync |
TIMESTAMP_LTZ | When this data was last synchronized from the source system. |
customer_v1
Customer properties. Each Matchi user has a unique user_id that identifies their account across all facilities. When a user plays at a facility, they are assigned a unique internal customer_id and a customer_number that is non-unique across facilities. The customer_number is what is displayed in the Matchi administration web interface. The customer_number is unique within each facility, but not across all facilities. The customer_id is unique within each facility and within matchi. It is the primary key for the customer table. A user will have multiple customer_ids if they play at multiple facilities.
Example: Player 'Mats Edberg' has user_id 12345 for his Matchi account. At venue X, he has customer_id 4995 and customer_number 55. At venue Y, he has customer_id 50123 and, by coincidence, also has customer_number 55.
Changelog:
- 2025-05-06 Added fullname
- 2025-01-03 Added telephone, cellphone, address, city, zipcode
📋 Table Structure
| Column | Type | Description |
|---|---|---|
address1 |
TEXT | First line of the customer's postal address. |
address2 |
TEXT | Second line of the customer's postal address. |
archived |
BOOLEAN | true if the customer has been archived at the facility. |
cellphone |
TEXT | The customer's mobile phone number. |
city |
TEXT | The customer's city. |
country |
TEXT | The customer's country as a two-letter ISO code (SE, NO, FI, DK, GB, ...). NULL when not set. |
customer_id |
INTEGER | (PK) The unique identifier for the customer. |
customer_number |
INTEGER | The venue specific identifier for the customer. |
date_created |
TIMESTAMP_NTZ | The timestamp when the customer was created at the facility. |
email |
TEXT | The customer's email address. |
facility_id |
INTEGER | (FK to facility) The facility that the customer belongs to. The same person has a separate customer record at each facility; use user_id to link them. |
firstname |
TEXT | The customer's first name. |
fullname |
TEXT | The customer's full name (first name and last name). |
gender |
TEXT | MALE, FEMALE, OTHER, NOT_SPECIFIED, ORGANIZATION or 'n/a' when not set. |
lastname |
TEXT | The customer's last name. |
telephone |
TEXT | The customer's phone number (landline). |
user_id |
INTEGER | The unique identifier for the matchi user. |
zipcode |
TEXT | The customer's postal code. |
latest_sync |
TIMESTAMP_LTZ | When this data was last synchronized from the source system. |
court_v1
Court properties. One row per court.
📋 Table Structure
| Column | Type | Description |
|---|---|---|
archived |
BOOLEAN | true if the court has been archived by the venue and is no longer bookable. |
camera_equipped |
BOOLEAN | true if the court has MATCHi TV cameras installed. |
court_id |
INTEGER | (PK) The unique identifier for the court. |
court_location |
TEXT | The location (indoor or outdoor) of the court. |
court_name |
TEXT | The name of the court as shown to players. |
court_type |
TEXT | Double/Standard/Mini etc |
date_created |
TIMESTAMP_NTZ | The timestamp when the court was created. |
description |
TEXT | Free-text description of the court, entered by the venue. |
facility_id |
INTEGER | (FK to facility) The facility that the court belongs to. |
last_updated |
TIMESTAMP_NTZ | The timestamp when the court was last updated. |
restriction |
TEXT | Booking restriction applied to the court. Useful for excluding non openly-bookable courts when calculating online occupancy. One of: - NONE: no restriction; bookable by anyone online. - OFFLINE_ONLY: not bookable online; only facility admins can book it (e.g. courts reserved for walk-ins or internal scheduling). - MEMBERS_ONLY: only customers with an active membership can book online. - REQUIREMENT_PROFILES: requires a specific player profile (see profiles). |
sport_name |
TEXT | The sport played on the court (e.g. Padel, Tennis, Badminton). |
surface |
TEXT | Hard/Clay/Carpet/Lawn etc |
latest_sync |
TIMESTAMP_LTZ | When this data was last synchronized from the source system. |
coupon_v1
Coupon properties. Note that coupon is a collective name and there are three classes of coupons:
- promo_code (applied at checkout give % off price)
- coupon (punch card with specific nr of tickets that are valid for a booking)
- gift_card (Like a wallet with a prepaid amount, used to pay for booking)
Changelog:
- 2025-01-06 Added as a new view.
📋 Table Structure
| Column | Type | Description |
|---|---|---|
archived |
BOOLEAN | true if the coupon has been archived by the venue and can no longer be sold or used. |
class |
TEXT | promo_code, coupon, gift_card |
code |
TEXT | promo code string e.g. 'SUMMER2025'. |
coupon_id |
INTEGER | (PK) The unique identifier for the coupon. |
coupon_name |
TEXT | The name of the coupon as set by the venue. |
date_created |
TIMESTAMP_NTZ | The timestamp when the coupon was created. |
description |
TEXT | Free-text description of the coupon, entered by the venue. |
start_date |
DATE | The start date of the coupon. |
end_date |
DATE | The end date of the coupon. |
facility_id |
INTEGER | (FK to facility) The facility that the coupon belongs to. |
max_duration |
INTEGER | How many minutes the coupon can be applied to (30, 60, 90, 120, ...) |
max_number_of_usages |
INTEGER | For promo codes, max number of usages per customer. |
nr_of_days_valid |
INTEGER | Ho long it is valid (coupon and gift card) |
unlimited |
BOOLEAN | true if the coupon can be used an unlimited number of times. |
vat_value |
NUMBER(38,6) | The VAT rate in percent applied to the coupon (e.g. 6, 25). NULL when no VAT rate is set. |
latest_sync |
TIMESTAMP_LTZ | When this data was last synchronized from the source system. |
Fact Tables
Fact tables contain measurable business events and transactions. They are used to define metrics like revenue, number of bookings, or membership counts. Each row represents a specific event (like a booking or payment), which is why they typically have multiple rows per entity (e.g., multiple bookings per customer).
activity_v1
One row for each activity occasion. Includes metadata about the activity and participation in the activity.
Changelog:
- 2025-01-13 Added end_time, time_slot, time_slot_desc, level, price, members_price, active_queue, player_in_queue, deleted, deleted_hours_before, status, fill_rate, active_members_price
- 2024-12-21 Added as a new view.
📋 Table Structure
| Column | Type | Description |
|---|---|---|
facility_id |
INTEGER | (FK to facility) |
activity_id |
INTEGER | The activity identifier. |
activity_occasion_id |
INTEGER | (PK) The unique identifier for the activity occasion. |
date |
DATE | The date of the activity occasion. |
start_time |
TIME | The start time (clock time, local) of the activity occasion. Combine with date for the full timestamp. |
end_time |
TIME | The end time (clock time, local) of the activity occasion. Combine with date for the full timestamp. |
time_slot |
TEXT | Night, Morning, Pre-lunch, Lunch, Afternoon, Evening, Pre-night. Based on start_time. |
time_slot_desc |
TEXT | 0. Night (00:00 - 06:00), 1. Morning (06:00 - 09:00), ... Sortable variant of time_slot with the time range. |
duration |
INTEGER | The length of the activity occasion in minutes. |
sport_name |
TEXT | The sport of the activity (e.g. Padel, Tennis). |
name |
TEXT | The name of activity. e.g. Volley training, Mexicano level 4-6,.... |
activity_visibility |
TEXT | Public (anyone can sign up) or Private (invitation only). |
activity_occasion_type |
TEXT | TRAINING, COMPETITION,... |
activity_event_name |
TEXT | AMERICANO, BEAT_THE_BOX,... |
external_service |
TEXT | External service the activity is integrated with, e.g. PADELBOARD. NULL if none. |
level |
TEXT | The skill level range the activity is intended for, formatted as min-max (e.g. 4.000000-6.000000). NULL if no level is set. |
price |
NUMBER(38,6) | The price to participate, including VAT, in the facility's currency. |
active_members_price |
BOOLEAN | If members get a discount on the activity. |
members_price |
NUMBER(38,6) | The price for members, including VAT. NULL if members pay the regular price. |
min_num_participants |
INTEGER | The minimum number of participants required for the activity to take place. |
max_num_participants |
INTEGER | The maximum number of participants allowed. |
players |
INTEGER | The number of players signed up for the activity occasion. |
paying_players_through_matchi |
INTEGER | The number of signed-up players who paid for the occasion through MATCHi. |
active_queue |
BOOLEAN | If there are players in queue for the activity. |
players_in_queue |
INTEGER | The number of players on the waiting list. |
nr_of_courts |
INTEGER | The number of courts reserved for the activity occasion. |
deleted |
BOOLEAN | true if the activity occasion was deleted (cancelled) by the venue. Only occasions deleted less than 7 days (168 hours) before their scheduled start are included; occasions deleted earlier than that are not in the share. |
deleted_hours_before |
INTEGER | For deleted occasions, the number of hours between the deletion and the scheduled start (0-168, see deleted). NULL if the occasion was not deleted. |
status |
TEXT | Completed activity , Automatic cancellation , Manual cancellation |
fill_rate |
TEXT | Filled, No participants, Below min participants, Above min participants |
latest_sync |
TIMESTAMP_LTZ | When this data was last synchronized from the source system. |
booking_v1
Fact table tracking all non-cancelled bookings. Each row represents a single booking session, including metadata about the booking type, timing, and participants.
Key metrics:
- Total number of bookings
- Total played hours
- Booking distribution by type and time slot
Note: Bookings are classified by booking_group_type (BOOKING, ACTIVITY, TRAINING, COMPETITION) and can be made by either the facility or the player.
Note: Bookings on archived courts and on days when the facility was inactive are retained — they are real events tied to orders and payments. The schedule view excludes archived courts from their archival date onwards and inactive facilities for the days they were inactive, so booking hours and schedule played_hours can differ marginally for venues with such activity.
Changelog:
- 2026-10-21 Added payment_status
- 2026-05-19 Added booking_client_id
- 2026-05-12 Added customer_category_name, membership_type_name
📋 Table Structure
| Column | Type | Description |
|---|---|---|
facility_id |
INTEGER | (FK to facility) |
booking_id |
INTEGER | (PK) The unique identifier for the booking. |
court_id |
INTEGER | (FK to court) |
customer_id |
INTEGER | (FK to customer) |
date_created |
TIMESTAMP_NTZ | The timestamp when the booking was created. |
slot_start_time |
TIMESTAMP_NTZ | The start timestamp of the booked slot. Local time. |
slot_end_time |
TIMESTAMP_NTZ | The end timestamp of the booked slot. Local time. |
duration |
INTEGER | The length of the booking in minutes. |
booking_group_type |
TEXT | SUBSCRIPTION, ACTIVITY, BOOKING, TRAINING, COMPETITION, ... |
booked_by |
TEXT | FACILITY, PLAYER |
latest_paid_membership |
INTEGER | (FK to membership) The latest membership that was active at the time of the booking. |
customer_category_name |
TEXT | The customer category applied by the pricing engine for this booking (e.g. "Standard", "Adult", "Junior", "Senior"). Reflects which category was actually used to determine the price, not what the customer selected at checkout. Available from 2026-03-05 onwards; NULL for older bookings. |
membership_type_name |
TEXT | The name of the membership type applied to the booking (e.g. "Premium", "Senior", "Game4Padel Membership"). Indicates which membership granted the price/access used for this booking. Available from ~2025-03 onwards; NULL for older bookings or when no membership was applied. |
time_slot |
TEXT | Night, Morning, Pre-lunch, Lunch, Afternoon, Evening, Pre-night. Based on slot_start_time. |
time_slot_description |
TEXT | 0. Night (00:00 - 06:00), 1. Morning (06:00 - 09:00), ... Sortable variant of time_slot with the time range. |
day_name |
TEXT | Mon, Tue, Wed, ... |
day_name_sort |
TEXT | 1. Mon, 2. Tue, 3. Wed, ... Sortable variant of day_name. |
weekend |
BOOLEAN | true (Sat-Sun), false (Mon-Fri) |
session_type |
TEXT | private, public |
sport_name |
TEXT | The sport of the booked court (e.g. Padel, Tennis). |
mtv_stream |
BOOLEAN | true if the booking is streamed on matchi tv |
order_origin |
TEXT | web, api (matchi app ar external api), facility |
payment_status |
TEXT | Whether the booking is paid, by the same rule the venue admin uses for "Booked - not paid". Set for booking_group_type BOOKING only: 'paid' (the payments registered in MATCHi cover the price, including bookings the venue marked as paid and free bookings), 'unpaid' (nothing, a partial amount or a credited-back payment is registered; a desk payment never marked as paid stays unpaid), 'not_applicable' (any other booking type: subscription, blocked time, training, competition, activity and match slots). As of the latest sync, so a booking later marked as paid moves from 'unpaid' to 'paid'. |
booking_client_id |
TEXT | OAuth client identifier of the integration that created the booking. NULL = booked via the matchi.com marketplace (or through the facility admin UI). 'matchi-mobileapp' = booked via the Matchi mobile app. Any other value = booked via an external integration or the venue's own booking widget. Useful for separating widget bookings from marketplace bookings when calculating occupancy, attribution, or commission. |
activity_occasion_id |
INTEGER | (FK to activity occasion) |
latest_sync |
TIMESTAMP_LTZ | When this data was last synchronized from the source system. |
customer_participation_log_v1
Fact table tracking customer participation across all venue activities. Each row represents a single participation event, including bookings, activities, and courses.
This table provides a comprehensive view of customer engagement with venue services, combining data from multiple sources:
- Court bookings (regular and subscription bookings)
- Activity participation
- Course participation
Key characteristics:
- One row per participation event
- Tracks participation across different activity types
- Records membership status at time of order creation
Use cases:
- Customer engagement analysis
- Participation pattern tracking
Changelog:
- 2026-10-21 Added payment_status
- 2026-09-10 Added age_group (age band of the customer at the time of participation)
- 2026-05-12 Added customer_category_name, membership_type_name
- 2025-10-22 Added member_at_order_date, removed currently_active_membership
- 2025-10-03 Added as new view for external data sharing
📋 Table Structure
| Column | Type | Description |
|---|---|---|
date |
TIMESTAMP_NTZ | The date and time when the participation event occurred. |
order_date_created |
TIMESTAMP_NTZ | The timestamp when the order was created. |
sport_name |
TEXT | The sport associated with the participation event. |
court_location |
TEXT | The location of the court (indoor/outdoor). |
customer_id |
INTEGER | (FK to customer) The unique identifier for the customer. |
age |
INTEGER | The age of the customer at the time of participation. |
age_group |
TEXT | The customer's age band AT THE TIME OF PARTICIPATION, bucketed from age. Point-in-time: a 17-year-old playing in 2025 stays in 13-17 for those rows even after turning 18. Use this to follow e.g. junior participation over time. Age bands: 0-12, 13-17, 18-24, 25-34, 35-44, 45-54, 55-64, 65+. The youth split follows the GDPR digital-consent age (13) and legal majority (18); adult bands follow Eurostat broad age groups. NULL when no birth date is known or the implied age is outside 0-105 (sentinel or mistyped birth dates); the age column is not clamped the same way, so a row can carry an implausible age with a NULL age_group. Coverage note: birth date has been mandatory in the MATCHi app since spring 2026, so coverage is close to complete for customers active since then and lower for older, dormant customers. The birth date is taken from the MATCHi user profile, falling back to the venue-entered date of birth on the customer record (e.g. juniors registered by the venue without their own account). Booking participations require a MATCHi account, so the venue-entered fallback only applies to activity and course participations. |
article |
TEXT | The type of participation ('BOOKING_OWNER', 'BOOKING_PARTICIPANT', 'ACTIVITY', 'COURSE', etc.). |
session_type |
TEXT | The type of session (private, public, etc.). |
booking_id |
INTEGER | (FK to booking) The booking identifier, if applicable. |
activity_occasion_id |
INTEGER | (FK to activity occasion) The activity occasion identifier, if applicable. |
facility_id |
INTEGER | (FK to facility) The facility where the participation occurred. |
customer_participation_number |
INTEGER | Sequential number of participation events for this customer. |
latest_paid_membership |
INTEGER | (FK to membership) The latest membership that was active at the time of participation. |
member_at_order_date |
BOOLEAN | Whether the customer had an active membership at the time of order creation. |
customer_category_name |
TEXT | The customer category applied by the pricing engine for the underlying order (e.g. "Standard", "Adult", "Junior", "Senior"). Reflects which category was used to determine the price, not what the customer selected at checkout. Available on BOOKING participations from 2026-03-05 onwards; NULL for older rows and for non-BOOKING participations. |
membership_type_name |
TEXT | The name of the membership type applied to the underlying order (e.g. "Premium", "Senior", "Game4Padel Membership"). Indicates which membership granted the price/access used. Available on BOOKING and ACTIVITY participations from ~2025-03 onwards; NULL otherwise. |
payment_status |
TEXT | Whether the order behind the participation is paid, by the same rule the venue admin uses: 'paid' (the payments registered in MATCHi cover the price, including orders the venue marked as paid and free orders), 'unpaid' (nothing, a partial amount or a credited-back payment is registered), 'not_applicable' (SUBSCRIPTION_BOOKING and COURSE rows, and sign-ups without an order). BOOKING_OWNER and BOOKING_PARTICIPANT rows reflect the whole court booking (same rule and BOOKING gate as booking_v1), so a participant who paid their share reads 'unpaid' if the booking as a whole is not. ACTIVITY and MATCH rows carry the participant's own order. As of the latest sync. Cancelled and annulled orders are not in this view. |
facility_home_vs_visitors_v1
Aggregated data showing home versus visiting player activity for each facility by month.
This table analyzes player engagement patterns by classifying players as either 'home' or 'visiting' based on their main facility. A player's main facility is determined using a rolling 12-month window of activity.
Key metrics:
- Number of bookings and activities
- Total money spent by player type
- Monthly trends in home vs visitor behavior
Use cases:
- Analyze visitor attraction patterns
- Compare home player loyalty vs visitor conversion
- Track facility attractiveness to non-members
- Measure cross-facility player traffic
Note: A player is classified as 'home_player' when they are at their main facility, and 'visiting_player' when at any other facility.
Changelog:
- 2026-08-18 Declared bookings_and_activities as INTEGER (was untyped NUMBER)
- 2025-10-02 Added as a new view
📋 Table Structure
| Column | Type | Description |
|---|---|---|
facility_id |
INTEGER | (FK to facility) The facility where the activity occurred. |
month_start |
DATE | The first day of the month for this aggregation. |
player_relation |
TEXT | Classification of the player: 'home_player' (playing at their main facility) or 'visiting_player' (playing at a different facility). |
bookings_and_activities |
INTEGER | Total count of bookings and activities for this facility-month-player_relation combination. |
money_spent |
NUMBER(19,2) | Total amount spent in local currency for this facility-month-player_relation combination. |
latest_sync |
TIMESTAMP_LTZ | When this data was last synchronized. |
membership_history_v1
Daily snapshot of membership status over time, designed for time-series analysis, for example in Steep metrics.
This table provides one row per customer per membership type per day, tracking how memberships evolve over their lifecycle. It identifies first and last dates of membership periods using LAG/LEAD window functions.
Key characteristics:
- Daily granularity for membership state tracking
- Identifies membership period boundaries (first/last dates)
- Covers full membership history, up to (not including) today
- Only includes fully paid, completed/confirmed memberships
The state column indicates the position in a membership period:
- 'First_date_as_member': First day of a membership period
- 'Member': Active membership day (not first or last)
- 'Last_date_as_member': Last day of a membership period
Changelog:
- 2026-08-18 The 2-year window was removed; the view now covers full history. Rows at the old 2-year edge that were left-censored as 'First_date_as_member' now resolve to their true state
- 2026-07-10 Added first_membership_start_date as cohort anchor for member retention; restricted date_actual to dates before today (date_actual < current_date; previously included today and future dates up to end_date)
- 2025-01-27 Expanded to all data share customers (previously limited to a few customers)
📋 Table Structure
| Column | Type | Description |
|---|---|---|
date_actual |
DATE | The calendar date for this membership snapshot. |
membership_id |
INTEGER | (FK to membership) The unique identifier for the membership. |
facility_id |
INTEGER | (FK to facility) The facility where the membership is valid. |
customer_id |
INTEGER | (FK to customer) The customer that holds the membership. |
type_id |
INTEGER | (FK to type) The membership type identifier. |
state |
TEXT | Position in membership period: 'First_date_as_member', 'Member', or 'Last_date_as_member'. |
first_membership_start_date |
DATE | The start date of the customer's first membership within the shared facility scope, computed over the full membership history. Constant per customer. Intended as cohort anchor for member retention analysis: count distinct customers cohorted by this date shows how many members from each starting period still hold an active membership N periods later, independent of renewals creating new membership rows. |
membership_v1
Fact table tracking membership transactions and their current status. Each row represents a single membership record, whether active, inactive, or in grace period.
Key characteristics:
- One row per membership record
- Tracks both single and family memberships
- Records payment status and membership state
Changelog:
- 2025-11-12 Added purchased_at_facility which show the name of the facility where the membership was purchased.
- 2025-02-17 Added category, order_status, actual_price
- 2025-01-03 Added grace_period_end_date
- 2024-11-28 Added as a new view.
📋 Table Structure
| Column | Type | Description |
|---|---|---|
membership_id |
INTEGER | (PK) The unique identifier for the membership. |
customer_id |
INTEGER | (FK to customer) The customer that the membership belongs to. |
facility_id |
INTEGER | (FK to facility) The facility where the memberships is valid |
purchased_at_facility_id |
INTEGER | (FK to facility) The facility that the membership was purchased at. |
purchased_at_facility |
TEXT | The name of the facility where the membership was purchased. For group member customers, this may differ from the facility where the membership is valid. |
state |
TEXT | grace_period, membership, inactive |
type_id |
INTEGER | The membership type identifier. All memberships of the same type share this id and type_name. |
type_name |
TEXT | The name of the memberhip given by the venue |
category |
TEXT | family, single |
start_date |
DATE | The first date the membership is valid. |
end_date |
DATE | The last date the membership is valid, before any grace period. |
grace_period_end_date |
DATE | The last date of membership privileges. |
payment_date |
DATE | The date the membership was paid. NULL if no payment has been recorded (e.g. free or unpaid memberships). |
is_active |
BOOLEAN | true if the membership is currently active |
is_final_paid |
BOOLEAN | true if the membership is fully paid |
payment_type |
TEXT | a list of how the membership was paid for (cash, adyen, invoice, ...) |
locality |
TEXT | local (valid at a single facility) or group (valid across the facility group). |
family_contact_customer_id |
INTEGER | (FK to customer) The customer that is the contact for the family membership. |
contact_email |
TEXT | The email address of the membership contact. For family memberships this is the family contact, otherwise the customer's own email. |
order_id |
INTEGER | The order through which the membership was purchased. |
order_status |
TEXT | COMPLETED, ANNULLED, CANCELLED |
actual_price |
number(38,6) | The true price of the membership for family memberships. Family contact has a non-zero price. |
latest_sync |
TIMESTAMP_LTZ | When this data was last synchronized from the source system. |
revenue_features_v1
One row per order_id with enrichment features for revenue data. Designed to be extensible — any order-level attribute can be added here. Currently contains membership dimensions (category, locality). When an order has multiple memberships, the most common value is selected (mode).
📋 Table Structure
| Column | Type | Description |
|---|---|---|
order_id |
INTEGER | (PK) The order identifier. |
facility_id |
INTEGER | (FK to facility) The facility associated with the membership order. |
category |
TEXT | Membership category (e.g. 'Family', 'Single'). Mode value when multiple memberships exist on the same order. |
locality |
TEXT | Membership locality (e.g. 'group', 'local'). Mode value when multiple memberships exist on the same order. |
revenue_v1
The revenue facts table shows all revenue transactions for each venue. Note that a single order can be paid in multiple transactions, resulting in multiple rows with the same order_id but different order_payment_id. The combination of order_id and order_payment_id is unique.
Orders with payment_type = cash have been marked as 'paid' by the venue in the Matchi administration interface. In practice, these may have been paid using any method; it simply means the payment did not go through Matchi. The invoice_date column indicates when the revenue is attributed to the venue, typically the latest of the payment date and delivery date.
Changelog:
- 2026-08-18 Added vat_amount, transaction_fee
- 2026-05-12 Added customer_category_name, membership_type_name
- 2025-01-13 Changed invoice_date from TIMESTAMP_NTZ to DATE
- 2025-02-12 Added promo_code, order_date
- 2025-01-03 Added num, num_credited, num_annulled, vat_pct, currency, sum_promo_code, latest_paid_membership
- 2024-12-21 Added activity_occasion_id
- 2024-11-28 Added booking_id, fullname, court_id, date_delivery, order_payment_id
- 2024-11-28 Removed facility name, user_id, firstname, lastname
📋 Table Structure
| Column | Type | Description |
|---|---|---|
facility_id |
INTEGER | (FK to facility) The facility that the revenue belongs to. |
customer_id |
INTEGER | (FK to customer) The customer that generated revenue. |
court_id |
INTEGER | (FK to court) The court that the revenue belongs to. |
activity_occasion_id |
INTEGER | (FK to activity occasion) The activity occasion that the revenue belongs to. |
booking_id |
INTEGER | (FK to booking) The booking that the revenue belongs to. |
order_id |
INTEGER | The order that the revenue belongs to. |
order_payment_id |
INTEGER | An order can have multiple payments. |
article |
TEXT | ('BOOKING', 'ACTIVITY', 'COUPON', 'MEMBERSHIP',...) |
num |
INTEGER | The number of items sold. |
num_credited |
INTEGER | The number of items credited. |
num_annulled |
INTEGER | The number of items annulled. |
price |
NUMBER(19,2) | The price of the item including VAT. |
amount |
NUMBER(19,2) | The amount paid for the item including VAT. |
vat_pct |
NUMBER(19,2) | The VAT (Value Added Tax) percentage. Controlled by the venue. |
latest_paid_membership |
INTEGER | (FK to membership) The latest membership that was active at the time of the order. |
customer_category_name |
TEXT | The customer category applied by the pricing engine for this order (e.g. "Standard", "Adult", "Junior", "Senior"). Reflects which category was actually used to determine the price, not what the customer selected at checkout. Available on BOOKING orders from 2026-03-05 onwards; NULL for older orders and for non-BOOKING articles. |
membership_type_name |
TEXT | The name of the membership type applied to the order (e.g. "Premium", "Senior", "Game4Padel Membership"). Indicates which membership granted the price/access used for this order. Available on MEMBERSHIP orders historically; on BOOKING and ACTIVITY orders from ~2025-03 onwards; NULL otherwise. |
sum_promo_code |
NUMBER(19,2) | The sum paid with the promo code including VAT. |
promo_code |
TEXT | The promo code that was used. |
coupon_id |
INTEGER | (FK to coupon) The coupon that was used. |
currency |
TEXT | SEK, NOK, DKK, EUR, GBP etc |
order_date |
TIMESTAMP_NTZ | The timestamp when the order was created. Local time. |
invoice_date |
DATE | The date when the revenue is attributed to the venue. |
date_delivery |
TIMESTAMP_NTZ | The date the purchased service was delivered, e.g. the date the booking was played. |
payment_type |
TEXT | How the order was paid: netaxept/adyen (online card and wallet payments), coupon (punch card), gift_card or cash. |
revenue |
NUMBER(19,2) | Venue revenue in local currency including VAT. |
vat_amount |
NUMBER(19,2) | The VAT portion of revenue in local currency, derived from vat_pct. Defined so that revenue - vat_amount equals revenue excluding VAT exactly per row. Rows where vat_pct is NULL are treated as 0% VAT. Accuracy depends on the vat_pct configured by the venue for each article, and the rate applies at order level. Not a substitute for VAT reporting; invoiced VAT actuals are in statement_data. |
transaction_fee |
NUMBER(19,2) | Matchi's transaction fee for the payment in local currency, excluding VAT. Calculated to correspond to the fee lines on the venue's monthly statement: includes gift card and punch card fees (usage and admin-created sales), and is zero for credited orders since those fees are withheld from the customer refund rather than billed. Invoiced actuals are in statement_data. |
latest_sync |
TIMESTAMP_LTZ | When this data was last synchronized from the source system. |
statement_data_v1
Combined statement data including both fees charged to customers and revenue paid out to venues. Each row represents a single invoice line item from either receivable or payable invoices.
This table combines data from two sources:
- Fees: Invoice lines for charges to customers (type='fees')
- Revenue: Invoice lines for payments to venues (type='revenue')
Key characteristics:
- All amounts are in local currency (facility currency)
- Revenue amounts are displayed as negative values to indicate outgoing payments
- Full statement history (no time limit); excludes today and future dates
- Includes both Sage invoice data (X3_INVOICLINES) and purchase invoice data (X3_PINVOICLINES)
Use cases:
- Venue revenue reconciliation
- Statement verification and auditing
- Fee and revenue analysis per facility
Changelog:
- 2026-08-18 Specified precision/scale on invoicerowno, article_nr (38,0) and quantity (38,6, matching the source scale)
- 2025-01-09 Added as new view combining fees and revenue statement data
📋 Table Structure
| Column | Type | Description |
|---|---|---|
facility_id |
TEXT | (FK to facility) The facility that the statement data belongs to. |
statementid |
TEXT | The statement identifier grouping related invoice lines. |
transaction_type |
TEXT | 'fees' (charged to customers) or 'revenue' (paid to venues) |
invoiceno |
TEXT | The invoice number from Sage system. |
invoicerowno |
NUMBER(38,0) | The line number within the invoice. |
currency |
TEXT | The currency of the amounts (SEK, EUR, NOK, DKK, etc.) |
article_nr |
NUMBER(38,0) | The Sage article number/product code. |
productdescr |
TEXT | The description of the product/service. |
quantity |
NUMBER(38,6) | The quantity of items invoiced. |
invoicedexvat |
DECIMAL(38,6) | The invoiced amount excluding VAT. Negative for revenue (outgoing payments). |
vatamount |
DECIMAL(38,6) | The VAT amount. Negative for revenue (outgoing payments). |
invoicedate |
TIMESTAMP_TZ | The date of the invoice. |
latest_sync |
TIMESTAMP_LTZ | When this data was last synchronized from the source system. |
Helper Tables
Helper tables provide additional functionality for data analysis and reporting.
date_spine
Helper table for date dimension. Can be used to join with other tables to get date dimension.
📋 Table Structure
| Column | Type | Description |
|---|---|---|
date_actual |
DATE | Format 'YYYY-MM-DD' |
day_name |
TEXT | Mon, Tue, Wed, ... |
month_actual |
INTEGER | 1,2,3,...,12 |
year_actual |
INTEGER | 2020, 2021, 2022, ... |
quarter_actual |
INTEGER | 1,2,3,4 |
day_of_week |
INTEGER | 1,2,3,...,7 |
month_name |
TEXT | January, February, March, ... |
monthly_coupon_value_v1
Helper table providing monthly snapshots of coupon values and status. One row per coupon per month, showing the value and status of all coupons at the end of each month.
Key information:
- Number of active coupons
- Remaining value of gift cards
- Remaining tickets for punch cards
- Total coupon distribution by status
Note: Join to coupon table to get the coupon details.
Suggested metric: Total gift card value = sum(remaining_amount) Suggested metric: Total remaining tickets = sum(remaining_nr_of_tickets)
📋 Table Structure
| Column | Type | Description |
|---|---|---|
coupon_id |
INTEGER | (FK to coupon) The coupon that the value belongs to. |
date |
DATE | The date of the value. |
facility_id |
INTEGER | (FK to facility) The facility that the coupon belongs to. |
nr_of_active_coupons |
INTEGER | The number of coupons that are active at the end of the month. |
nr_of_deleted_coupons |
INTEGER | The number of coupons that are deleted at the end of the month. |
nr_of_expired_coupons |
INTEGER | The number of coupons that are expired at the end of the month. |
nr_of_locked_coupons |
INTEGER | The number of coupons that are locked at the end of the month. |
remaining_amount |
NUMBER(19,6) | The remaining amount of active gift cards. |
remaining_nr_of_tickets |
INTEGER | The remaining number of tickets for active coupons. |
tot_nr_of_coupons |
INTEGER | The total number of coupons of this kind. |
latest_sync |
TIMESTAMP_LTZ | When this data was last synchronized from the source system. |
customer_features_v1
Helper table providing pre-calculated customer behavioral features and preferences. One row per customer.
This table contains derived metrics and features calculated from customer interaction history, designed to simplify customer analysis and reporting. It includes pre-computed metrics like preferred sports, activity patterns, and membership status.
Key features:
- Main sport preference based on booking history
- First and latest interaction timestamps
- Activity status and grouping
- Facility user status
- Current membership information
Note: The features are updated daily to reflect the latest customer behavior patterns.
Changelog:
- 2026-09-10 Added age_group (current age band of the customer)
- 2025-10-02 Added favorite_playing_time, participations_per_week_r12, player_segment, first_article
- 2025-01-03 Added current_inactive_days_grp
- 2024-12-21 Added as a new view
📋 Table Structure
| Column | Type | Description |
|---|---|---|
customer_id |
INTEGER | (PK) The unique identifier for the customer. |
facility_id |
INTEGER | (FK to facility) |
main_sport |
TEXT | Based on the customer's prior bookings. |
first_interaction |
TIMESTAMP_NTZ | The first time the customer made or joined a booking/actiivty/course. |
latest_interaction |
TIMESTAMP_NTZ | The latest time the customer made or joined a booking/actiivty/course. |
current_inactive_days |
INTEGER | The number of days the customer is inactive. |
current_inactive_days_grp |
TEXT | Groupinb based on the customer's inactive days. |
current_membership |
INTEGER | The current membership id of the customer. |
facility_user |
BOOLEAN | True if the customer is an administrator at the facility. |
favorite_playing_time |
TEXT | The customer's preferred playing time slot. |
participations_per_week_r12 |
NUMBER(19,2) | Average number of participations per week over the last 12 months. |
player_segment |
TEXT | The player segment classification for this customer. |
first_article |
TEXT | The first article type purchased by the customer. |
age_group |
TEXT | The customer's CURRENT age band, computed from their birth date as of today (it shifts as customers age). For age at the time of play use customer_participation_log_v1.age_group instead. Age bands: 0-12, 13-17, 18-24, 25-34, 35-44, 45-54, 55-64, 65+. The youth split follows the GDPR digital-consent age (13) and legal majority (18); adult bands follow Eurostat broad age groups. NULL when no birth date is known or the implied age is outside 0-105 (sentinel or mistyped birth dates); the age column is not clamped the same way, so a row can carry an implausible age with a NULL age_group. Coverage note: birth date has been mandatory in the MATCHi app since spring 2026, so coverage is close to complete for customers active since then and lower for older, dormant customers. The birth date is taken from the MATCHi user profile, falling back to the venue-entered date of birth on the customer record (e.g. juniors registered by the venue without their own account). |
latest_sync |
TIMESTAMP_LTZ | When this data was last synchronized from the source system. |
schedule_v1
Helper table providing pre-aggregated court occupancy metrics across different time dimensions. This table simplifies the calculation of court utilization by providing pre-calculated hours for each time slot.
The data is aggregated across multiple dimensions:
- Facility, date, and court
- Weekday and time slot
- Sport type
Time slots are grouped into larger buckets (e.g., 'Evening' for 16:00-22:00). Each slot has two key metrics:
bookable_hours: Total hours available for booking in the time slotplayed_hours: Total hours that have been booked
Note: Courts classified as 'Facility resource' (e.g., doors, office, ball machines) are excluded from this view.
Note: Slots on archived courts are excluded from their archival date onwards, so archived courts do not contribute bookable hours. History from before the archival date is kept.
Note: Slots on days when the facility was inactive are excluded, so deactivated venues do not contribute bookable hours. History from when the facility was active is kept.
Note: This view is not limited in time — it exposes full slot history back to 2012. No external share view carries a time limit.
To calculate occupancy (percentage of time courts are booked):
- Group by your desired dimensions (e.g., facility, date, time slot)
- Sum the
played_hoursandbookable_hours - Divide: sum(played_hours) / sum(bookable_hours)
Suggested metric: Occupancy = sum(played_hours) / sum(bookable_hours)
Changelog:
- 2026-10-05 Slots on days when the facility was inactive are now excluded
- 2026-08-18 Removed a 24-month time filter that never applied (it compared the date with itself); the share stays unbounded in time, as it always was
- 2026-08-03 Slots on archived courts are now excluded from the archival date onwards
📋 Table Structure
| Column | Type | Description |
|---|---|---|
facility_id |
INTEGER | (FK to facility) |
court_id |
INTEGER | (FK to court) |
activity_occasion_id |
INTEGER | (FK to activity occasion) |
sport_name |
TEXT | The sport of the court (e.g. Padel, Tennis). |
date |
DATE | The date of the schedule slot. |
booking_group_type |
TEXT | The state of the slot: UNBOOKED, UNAVAILABLE or the type of booking that occupies it (BOOKING, SUBSCRIPTION, TRAINING, ACTIVITY, COMPETITION, MATCH, ...). |
booked_by |
TEXT | Who made the booking: PLAYER (booked online by the player) or FACILITY (booked by venue staff). NULL for unbooked slots. |
day_name |
TEXT | Mon, Tue, Wed, ... |
day_name_sort |
TEXT | 1. Mon, 2. Tue, 3. Wed, ... |
weekend |
BOOLEAN | true (Sat-Sun), false (Mon-Fri) |
time_slot |
TEXT | Night, Morning, Pre-lunch, Lunch, Afternoon, Evening, Pre-night |
time_slot_description |
TEXT | Night (00:00 - 06:00), Morning (06:00 - 09:00),... |
bookable_hours |
NUMBER(19,2) | Number of hours that are available for booking. |
played_hours |
NUMBER(19,2) | Number of hours that have been booked |
latest_sync |
TIMESTAMP_LTZ | When this data was last synchronized from the source system. |