Skip to main content

Event modeling

An event table records what users do: one row for every action a user took, at the moment they took it. Funnels, retention, journeys and segmentation in Mitzu are all built from these rows, so the shape of your event tables decides what you can ask.

This page covers the columns an event table needs, the three layouts Mitzu supports, and how to choose between them. Properties that describe a user or a company rather than a single event belong in dimension tables. Numbers your data team already calculates per period belong in metric tables. There is more background in this blog post.

The columns of an event table​

ColumnRequiredWhat it holds
User IDYesThe ID of the user who performed the event. Every count of users, every funnel and every retention cohort is keyed on it.
Event timeYesWhen the event happened.
Event nameMulti event tables onlyThe name of the event, for example page_viewed or checkout_completed.
Entity ID columnsNoThe ID of another entity the event belongs to, such as the company, team or session.
Date partitionNoA date column the warehouse partitions the table by. Mitzu filters on it so queries only read the partitions they need.
Every other columnNoEvent properties, which you can filter and break down by.

Every event a user performs should be a single row. Mitzu counts rows, so a row that is duplicated by a load job is counted twice.

User ID​

Use the same ID for a user in every event table and in the user dimension tables. Mitzu joins on it as is.

If your tracking writes an anonymous ID before sign-in and a user ID after it, resolve the two in the warehouse before Mitzu reads the table. A view that picks the resolved ID per row works well:

SELECT
coalesce(identity_map.user_id, events.anonymous_id) AS user_id,
events.*
FROM raw.events AS events
LEFT JOIN analytics.identity_map AS identity_map
ON identity_map.anonymous_id = events.anonymous_id

Event time​

Use a timestamp column without a time zone, holding every event in the same zone, ideally UTC. Unix epoch numbers in seconds, milliseconds or microseconds also work on most warehouses. Where you can choose, use the client time corrected by the server clock, which most CDPs store as the event's timestamp. Supported data types has the recommended type for every role.

Entity ID columns​

An event can belong to more than one thing: the user who clicked, the company they work for, the workspace they were in. Add an entity for each of these, then set its ID column on the event table. Mitzu can then:

  • count unique companies (or any other entity) instead of unique users,
  • join that entity's dimension tables, so you can filter and break down events by company properties such as industry or plan.

A column only needs to exist on the event tables where it makes sense. Events without a company ID simply have no company properties.

Event properties​

Every column that isn't one of the roles above becomes an event property. Columns with nested types (JSON, MAP, VARIANT and STRUCT) are expanded: Mitzu reads the keys during indexing and lists each one as its own property, so context.page.url can be filtered like a plain column.

Use Ignore columns on the event table settings for columns that shouldn't show up as properties, such as internal load metadata or personal data you don't want analysts to filter on.

Three event table layouts​

Mitzu supports three ways of laying out events. They can be mixed: a workspace can have single event tables from one source and a multi event table from another.

Single event tablesWide multi event tableShort multi event table
TablesOne per eventOne for all eventsOne for all events
Event name comes fromThe table nameThe event name columnThe event name column
Properties live inThat table's columnsColumns shared by all eventsA JSON, MAP or VARIANT column
Typical sourceSegment and RudderStack warehouse loads, dbt models per eventSnowplow's events table, "one big table" dbt modelsExports from analytics tools, custom tracking pipelines
Main costMany tables to configureMany sparse columnsNested values are slower to read

Single event tables​

Each event has its own table, and the table name is the event name. A checkout_completed table holds one row per completed checkout:

user_idevent_timeorder_idamountcurrency
u_422026-09-01 10:14:03o_98149.00EUR
u_172026-09-01 10:20:51o_982120.00USD

Each table needs a user ID column and an event time column. The other columns are properties of that event only, so each table stays narrow and easy to read.

CDPs such as Segment and RudderStack write events this way, so you can point Mitzu at their schema and add every table at once. Select the tables with Add all <N> tables on the event tables page, and set the user ID and event time column names once for all of them.

tip

The same CDPs also write a combined table, such as tracks, with one row per event of every type. Add either the per-event tables or the combined table, not both, otherwise each event appears twice in the event list.

Wide multi event tables​

All events share one table, told apart by an event name column. Each property gets its own column, and a column is empty for events that don't set it:

user_idevent_timeevent_namepage_urlorder_idamount
u_422026-09-01 10:12:40page_viewed/pricing
u_422026-09-01 10:14:03checkout_completedo_98149.00

Every value of the event name column becomes an event in Mitzu. Mitzu also works out which properties belong to which event during indexing, so the amount property only shows up for events that set it.

warning

A wide table often grows to 100+ columns, and every new property means a schema change. It is easiest to maintain when a dbt model or a similar job builds it.

Short multi event tables​

All events share one table, and the properties of each event are stored together in a single JSON, MAP or VARIANT column:

user_idevent_timeevent_nameproperties
u_422026-09-01 10:12:40page_viewed{"page_url": "/pricing"}
u_422026-09-01 10:14:03checkout_completed{"order_id": "o_981", "amount": 49.0}

New properties need no schema change. Mitzu expands the keys of the properties column into separate event properties, just as it does with the columns of a wide table.

warning

Reading values out of JSON, MAP or VARIANT columns is slower than reading plain columns. If a few properties are used in almost every insight, such as the plan or the platform, copy them into their own columns.

Which layout to choose​

  • Use the layout you already have. Mitzu works on raw data, so start with whatever your CDP or pipeline writes and remodel only when you hit a limit.
  • Single event tables suit a moderate number of events with distinct properties, and they are the easiest to keep fast.
  • A multi event table suits hundreds of event types, or event names that change often: a new event appears after the next indexing without anyone adding a table.
  • Short over wide when properties change often. Wide over short when the property set is stable and query speed matters most.

Performance​

Event tables are usually the largest tables Mitzu reads. These choices keep queries fast and warehouse costs down:

  • Partition or cluster by event time. Every insight filters on a date range. Set the partition column as the Date partition column on the event table so Mitzu adds a filter the warehouse can prune on.
  • Cluster multi event tables by event name. Most insights read only a few events out of hundreds.
  • Keep the user ID cheap to compare. Joins and unique counts are on this column, so a plain string or integer is faster than a value pulled out of JSON.
  • Materialize expensive views. A view is recomputed every time it is queried. If a view joins or unnests large tables, build it as a table or a materialized view instead.
  • Optimize lakehouse tables. For Iceberg and Delta Lake, compact small files and keep table statistics current.

Performance settings covers the Mitzu side, such as event and entity sampling.

Checklist​

  • One row per event, without duplicates.
  • A user ID that matches the user dimension tables and the other event tables.
  • An event time column, in one time zone.
  • An event name column on multi event tables.
  • An ID column for each entity you want to count or describe, such as the company.
  • A partition or cluster key on event time, set as the date partition column.
  • Columns you don't want to expose listed under Ignore columns.

Next: describe users and companies with dimension tables, or add your event tables.