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
| Field | What it holds | What it cannot tell you |
|---|---|---|
| event_date, event_timestamp | A YYYYMMDD string, and the receipt time in microseconds UTC | The user's local time, or when the interaction happened rather than arrived |
| event_name | The name your own implementation chose | Anything about its schema — 2 rows sharing a name can carry different parameters |
| event_params | A repeated key plus a value struct: string_value, int_value, double_value, float_value | Which slot is populated, or that a key keeps the same type across rows |
| user_pseudo_id | A pseudonymous identifier scoped to the browser or app install | A person. It changes when storage is cleared, and may be absent entirely |
| user_id | Your own identifier, on the events where you set it | Anything about the events where you did not |
| privacy_info | The consent state recorded at collection, including a transient-token flag | The user's current preference, which may have changed since |
| traffic_source | The user's first-acquisition channel, fixed at their first visit | The source of this event, or of the visit it belongs to |
| items, ecommerce | Repeated item rows and the order-level revenue fields you sent | Whether any of it reconciles with your finance ledger |
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
The work behind this page
Builds from our portfolio that this page draws on.
PipelineIQ
An AI SDR platform that scores every lead for fit, runs multichannel sequences across email, LinkedIn and call, drafts the replies, and books the meeting.
Sales AIAskVault
An AI internal knowledge-search platform that answers employee questions from your own docs — grounded in citations, with knowledge gaps surfaced and deflection tracked.
Productivity AIRead next
- Consent state: a typed field on each event, not a switch on the pageThe pageview before the banner answer and the purchase after it are both correct, and they carry different consent values. That only works if consent travels per event.definition
- Event ID: the value that lets two systems agree a conversion happened onceThe same identifier, the same conversion, both transports, inside the platform's window. Four generation strategies, and the guarantee each one keeps or breaks.definition
- The tagging server: a first-party endpoint that owns the payloadA tagging server is not a proxy. It parses one first-party event, adds and strips fields, and emits a vendor-shaped request per destination from your infrastructure.definition
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