Marketing, Advertising & AdTech// definition

The exported event row: nested parameters and no session column

In short

A raw analytics event export is an append-only fact table: one row per event, no users, no sessions, no metrics stored anywhere in it. Sessions and conversion rates are derived at query time from rows carrying an event name, a microsecond timestamp, a repeated parameter struct and a pseudonymous identifier that can be absent. An export cannot match an interface figure by construction.

Key takeaways

  • The export stores facts, not figures. Sessions, users and every rate in a report are computed at query time, by rules you now own.
  • There is no session column. The session identifier arrives as a parameter inside the repeated event_params struct.
  • event_params.value has 4 typed slots — string, int, double and float — and only one is filled per row, so every read needs a coalesce.
  • user_pseudo_id identifies a browser, not a person, and can be absent entirely when analytics storage was denied.

One row of a raw analytics export is a single event that happened, and almost nothing else: a name, a timestamp in microseconds, a repeated bag of parameters, a device-scoped pseudonymous identifier and some context structs. No session, no user record, no metric. Everything a report shows is computed on top of these rows, which is the most useful fact about the table.

The practical consequence is worth accepting early: your SQL and the reporting interface apply different rules to the same events, so the two will not agree. Chasing an exact match is a project with no end. Deciding, writing down and version-controlling your own rules is a project that finishes.

One row, field by field

FieldWhat it holdsWhat it cannot tell you
event_date, event_timestampA YYYYMMDD string, and the receipt time in microseconds UTCThe user's local time, or when the interaction happened rather than arrived
event_nameThe name your own implementation choseAnything about its schema — 2 rows sharing a name can carry different parameters
event_paramsA repeated key plus a value struct: string_value, int_value, double_value, float_valueWhich slot is populated, or that a key keeps the same type across rows
user_pseudo_idA pseudonymous identifier scoped to the browser or app installA person. It changes when storage is cleared, and may be absent entirely
user_idYour own identifier, on the events where you set itAnything about the events where you did not
privacy_infoThe consent state recorded at collection, including a transient-token flagThe user's current preference, which may have changed since
traffic_sourceThe user's first-acquisition channel, fixed at their first visitThe source of this event, or of the visit it belongs to
items, ecommerceRepeated item rows and the order-level revenue fields you sentWhether any of it reconciles with your finance ledger
The load-bearing fields on an exported event row, and the question each one cannot answer

Two rows deserve emphasis. The parameter names are exactly the names your page emitted, which makes the export a permanent record of how disciplined the instrumentation was — the argument for treating the data layer as a versioned contract. And the click identifiers that attribute a sale to an ad arrive as parameters, not columns, which is why the parameters that tie a click to a sale must survive every redirect on the way in.

The three things that are not in the table

  • No session column. The session identifier arrives as a parameter inside event_params, so every session-scoped figure is built by a query — including where a session starts and whether it survives midnight.
  • No user dimension. There is no row per user. User attributes ride on every event and can differ between events for one identifier, so "the user's country" is a choice about which event to believe.
  • No metrics. Engaged sessions, conversion rate, bounce, average order value: none is stored. Each is a definition, and writing it in SQL means owning it.

Why the totals will not line up

A reporting interface applies its own session logic, attribution model, thresholds and, at volume, its own estimation. The export applies none of it. A difference is therefore expected, and the useful question is not its size but its stability: a constant offset is definitional, a moving one is usually a collection problem. Duplicate conversions only become visible at row level, which is how conversions counting twice after the server goes live gets confirmed rather than argued about.

The interface gives you a number and hides the definition. The export gives you the definition and makes you write the number. Only one of those can be audited.

This stored-facts-versus-derived-figures split is not unique to analytics. An auction log has the same shape: the number in the report was computed at decision time from rules never written on the row, as in hard and soft floors in an auction. The row is the evidence; the report is an opinion about it.

Building on raw event rows is ordinary data engineering: typed inputs, tested transformations, a scheduled job, an alert when a definition drifts — the pattern in AI in logistics operations and how we scope automation and pipeline work. The rest of the cluster sits under tracking, consent and event pipelines in the marketing and advertising practice.

Frequently asked questions

Short answers to the follow-ups this page tends to raise.

Why is there no session ID column in the event export?

Because a session is a reporting construct, not a collected fact. The identifier grouping events into a session is sent as a parameter by the tag, so it sits inside the repeated event_params struct. Anything session-scoped is reconstructed in the query, and the definition you choose — timeout length, whether a campaign change starts a new one, what happens across midnight — is yours to defend.

What does user_pseudo_id actually identify?

A browser or app install, not a person. It is written to storage on first visit, so it changes when the user clears storage, switches browser or moves device, and 1 human routinely appears as 3 or 4 identifiers. Under a denied analytics-storage state there may be no identifier at all, so a share of your rows can never be joined to any other row.

Why do the export numbers differ from the reporting interface?

Because the interface computes metrics with its own definitions, and the export stores only raw events. Session rules, attribution models, active-user thresholds and any estimation applied at scale live in the interface, not the table. Expect a gap, watch whether it is stable, and treat a sudden change in it as a collection issue rather than a query issue.

How do you read a value out of event_params?

Unnest the repeated field, filter to the key you want, then coalesce across the 4 typed value slots rather than reading one. Casting straight from a single slot is the commonest source of quietly wrong numbers: the populated slot depends on what the page sent, and that can change between releases without anything failing.

  • event export
  • data warehouse
  • analytics schema
  • SQL
// shipped work

The work behind this page

Builds from our portfolio that this page draws on.

Working on something in this space?

Tell us where you are in a sentence or two. We'll tell you honestly whether we're the right team, and what a sensible first slice of the work looks like.

Start the conversation