---
title: "GA360 Standard Data Model Template: 10 Data Marts | OWOX"
canonical: "https://www.owox.com/data-models/ga360-standard"
updated: "2026-11-14"
---

# GA360 Standard

10 data marts · 71 fields · [View on GitHub](https://github.com/OWOX/models/blob/main/bundles/ga360-standard/index.md)

The Google Analytics 360 export to BigQuery as ten Data Marts: sessions at the hub, with the page views, events and purchases that join them and the locations, devices, traffic sources, users, pages and event details behind them.

## Overview

A ready-made reporting layer over the Google Analytics 360 export to BigQuery. It turns the daily `ga_sessions_*` tables into ten analytics-ready tables: sessions, page views, events and purchases, plus the locations, devices, traffic sources, users, pages and event details they point at by hash. Two staging tables sit between the export and most tables: `_temp_objects` feeds page views, events, pages and event details, and `_temp_transactions` feeds purchases, on Transactions and on Sessions.

The SQL that builds the tables is part of the model, in references/sql-transformation, with a runbook for the first load and the daily run. It is written for the [OWOX Transformations plugin](https://github.com/OWOX/transformations-plugin) in the BigQuery (GoogleSQL) dialect, and you can adapt it to your needs.

Sessions are the hub, one row per session of the export. Page views, events and purchases join them on `date` and `sessionId` together. **The dimension tables carry no date**: each run appends the same hashes again, so deduplicate them before joining in your own SQL. **Revenue is in millionths**, as Google's export stores it: divide by 1,000,000.

## Example Questions

*   Which sources and campaigns bring the `sessions` that end in a purchase?
*   Which landing pages bring the most purchase revenue on mobile?
*   Which `pages` do visitors from each country view most?

[Explore on canvas →](https://model.owox.com/?okf=https://github.com/OWOX/models/tree/main/bundles/ga360-standard)

## Devices

The device a session ran on: category, browser and its version, operating system and its version, one row per combination, as the GA 360 export records them. Sessions point here through `deviceHash`, a FARM\_FINGERPRINT of the five values. The table has no date, so a hash repeats once per run (once per day for daily runs). Count distinct hashes. In your own SQL over these tables, deduplicate it before joining, or rows multiply; OWOX Data Marts reports collapse joined fields per source row.

### Fields

| Column | Type | Alias | Description |
| --- | --- | --- | --- |
| `deviceHash` | STRING | Device Hash | PK. FARM\_FINGERPRINT of the five device values in the order listed here, joined without a separator, with an empty value in place of NULL. Unique within one run; repeats across daily runs. |
| `deviceCategory` | STRING | Device Category | The device category, such as `desktop`, `mobile` or `tablet`. |
| `browser` | STRING | Browser | The browser, such as `Chrome` or `Safari`. |
| `browserVersion` | STRING | Browser Version | The version of the browser. |
| `operatingSystem` | STRING | Operating System | The operating system, such as `Windows`, `iOS` or `Android`. |
| `operatingSystemVersion` | STRING | Operating System Version | The version of the operating system. |

## Event Info

The category, action and label behind event hits, one row per combination, as the site sent them. Events point here through their `pageHash` column, which despite its name holds this table's `eventHash`, a FARM\_FINGERPRINT of the three values. Each run adds the combinations of its own window. The table has no date, so a hash repeats once per run (once per day for daily runs). Count distinct hashes. In your own SQL over these tables, deduplicate it before joining, or rows multiply; OWOX Data Marts reports collapse joined fields per source row.

### Fields

| Column | Type | Alias | Description |
| --- | --- | --- | --- |
| `eventHash` | STRING | Event Hash | PK. FARM\_FINGERPRINT of category, action and label, joined without a separator, with an empty value in place of NULL. Unique within one run; repeats across daily runs. Events carry it as `pageHash`. |
| `eventCategory` | STRING | Event Category | The event category the site sent. |
| `eventAction` | STRING | Event Action | The event action the site sent. |
| `eventLabel` | STRING | Event Label | The event label the site sent. Empty when the event had none. |

## Events

Every event hit of the GA 360 export, one row each, tied to the session it fell in. Join to Sessions on `date` and `sessionId` together. Despite its name, `pageHash` holds the event hash: it joins Event Info, which carries the event's category, action and label. `hitId` identifies an event: a rerun of the same window gives the same value, a different window does not, and Pageviews numbers its hits separately. `time` is a timestamp in milliseconds since 1970-01-01 UTC; `date` is the day in the property's time zone.

### Fields

| Column | Type | Alias | Description |
| --- | --- | --- | --- |
| `date` | DATE | Date | PK. The day of the export table the event was read from, in the property's time zone. |
| `sessionId` | STRING | Session ID | PK. The session the event belongs to: the full visitor ID and the visit ID, joined by `_`. Join to Sessions on this and `date` together. FK to [Sessions](#mart-sessions) |
| `hitId` | STRING | Hit ID | PK. MD5 of the session ID, the hit's time in the session and its order among the session's events. Stable for a rerun of the same window, not across different windows. |
| `time` | INTEGER | Event Time | Timestamp of the event, in milliseconds since 1970-01-01 UTC: the session start plus the hit's offset. |
| `events` | INTEGER | Events | Always 1. Sum it to count events. |
| `pageHash` | STRING | Event Hash | Despite its name, the event hash: FARM\_FINGERPRINT of the event's category, action and label. It joins `eventHash` in Event Info, not Pages. FK to [Event Info](#mart-event-info) |

### Relationships

| Related data mart | On | Cardinality | Meaning |
| --- | --- | --- | --- |
| [Sessions](#mart-sessions) | `date = date, sessionId = sessionId` | N:1 | The session the event fell in. |
| [Event Info](#mart-event-info) | `pageHash = eventHash` | N:1 | The event's category, action and label; the hash repeats across runs. |

## Geo

Where a session came from: country, region and city, one row per combination, as the GA 360 export records them. Sessions point here through `geoHash`, a FARM\_FINGERPRINT of the three values. The table has no date, so a hash repeats once per run (once per day for daily runs). Count distinct hashes. In your own SQL over these tables, deduplicate it before joining, or rows multiply; OWOX Data Marts reports collapse joined fields per source row.

### Fields

| Column | Type | Alias | Description |
| --- | --- | --- | --- |
| `geoHash` | STRING | Geo Hash | PK. FARM\_FINGERPRINT of country, region and city, joined without a separator, with an empty value in place of NULL. Unique within one run; repeats across daily runs. |
| `country` | STRING | Country | The session's country, as the export records it. |
| `region` | STRING | Region | The session's region or state, as the export records it. |
| `city` | STRING | City | The session's city, as the export records it. |

## Pages

The pages behind page views: host name, path and title, one row per combination. The path is cut at the first `?`, `#` or `;`, so query strings and anchors are dropped. The title is part of the key, so one path with two titles is two pages. Page views point here through `pageHash`, a FARM\_FINGERPRINT of host, path and title. Each run adds the pages of its own window. The table has no date, so a hash repeats once per run (once per day for daily runs). Count distinct hashes. In your own SQL over these tables, deduplicate it before joining, or rows multiply; OWOX Data Marts reports collapse joined fields per source row.

### Fields

| Column | Type | Alias | Description |
| --- | --- | --- | --- |
| `pageHash` | STRING | Page Hash | PK. FARM\_FINGERPRINT of host name, path and title, joined without a separator, with an empty value in place of NULL. Unique within one run; repeats across daily runs. |
| `hostName` | STRING | Host Name | The host name of the page, such as `www.example.com`. |
| `pagePath` | STRING | Page Path | The path of the page, cut at the first `?`, `#` or `;`. |
| `pageTitle` | STRING | Page Title | The title of the page, as the browser sent it. |

## Pageviews

Every page view hit of the GA 360 export, one row each, tied to the session it fell in. Join to Sessions on `date` and `sessionId` together, and to Pages on `pageHash` for the host, path and title: the table holds no page path itself. `hitId` identifies a page view: a rerun of the same window gives the same value, a different window does not, and Events numbers its hits separately. `time` is a timestamp in milliseconds since 1970-01-01 UTC; `date` is the day in the property's time zone.

### Fields

| Column | Type | Alias | Description |
| --- | --- | --- | --- |
| `date` | DATE | Date | PK. The day of the export table the page view was read from, in the property's time zone. |
| `sessionId` | STRING | Session ID | PK. The session the page view belongs to: the full visitor ID and the visit ID, joined by `_`. Join to Sessions on this and `date` together. FK to [Sessions](#mart-sessions) |
| `hitId` | STRING | Hit ID | PK. MD5 of the session ID, the hit's time in the session and its order among the session's page views. Stable for a rerun of the same window, not across different windows. |
| `time` | INTEGER | Page View Time | Timestamp of the page view, in milliseconds since 1970-01-01 UTC: the session start plus the hit's offset. |
| `pageview` | INTEGER | Pageviews | Always 1. Sum it to count page views. |
| `pageHash` | STRING | Page Hash | FARM\_FINGERPRINT of the page's host name, path and title. FK to [Pages](#mart-pages) |

### Relationships

| Related data mart | On | Cardinality | Meaning |
| --- | --- | --- | --- |
| [Sessions](#mart-sessions) | `date = date, sessionId = sessionId` | N:1 | The session the page view fell in. |
| [Pages](#mart-pages) | `pageHash = pageHash` | N:1 | The host, path and title of the page viewed; the hash repeats across runs. |

## Sessions

One row per session of the GA 360 export, with its landing page, engagement, purchases and the keys to its location, device, traffic source and user. A row is identified by `date` together with `sessionId`; page views, events and purchases join on both. `visitStartTime` is in seconds since 1970, and `timeOnSite` is in whole seconds with the fraction cut off, so a one-hit session shows 0. Purchases are deduplicated the same way as in Transactions. `transactionRevenue` is in millionths, as Google's export stores it: divide by 1,000,000. `cost`, `impressions` and `clicks` are always 0, slots for ad cost blended in later.

### Fields

| Column | Type | Alias | Description |
| --- | --- | --- | --- |
| `date` | DATE | Date | PK. The day of the export table the session was read from, in the property's time zone. |
| `sessionId` | STRING | Session ID | PK. The full visitor ID and the visit ID, joined by `_`. Pair it with `date`: page views, events and purchases join on both. |
| `landingPage` | STRING | Landing Page | Path of the session's first hit marked as an entrance, or of its first hit when none is, cut at the first `?`, `#` or `;`. |
| `landingHost` | STRING | Landing Host | Host name of the same hit as `landingPage`. |
| `visitStartTime` | INTEGER | Session Start Time | When the session started, in seconds since 1970-01-01 UTC, as the export records it. |
| `visitNumber` | INTEGER | Session Number | The visitor's session count as Google Analytics reports it: 1 for a first visit. |
| `touchpoint` | STRING | Touchpoint | Always `WEB`. |
| `dataSource` | STRING | Data Source | The export the session was read from, as `project.dataset.ga_sessions_*`. |
| `hits` | INTEGER | Hits | All hits in the session as the export counts them (`totals.hits`), hit types other than page views and events included. |
| `pageviews` | INTEGER | Pageviews | Number of page view hits in the session. |
| `events` | INTEGER | Events | Number of event hits in the session. |
| `bounces` | INTEGER | Bounces | 1 when the session has at most one hit that is a page view or an interactive event, otherwise 0. |
| `timeOnSite` | INTEGER | Time on Site (seconds) | Time from the session's first hit to its last, in whole seconds with the fraction cut off. 0 for a session with one hit. |
| `userHash` | STRING | User Hash | FARM\_FINGERPRINT of the session's client ID; the other user identifiers are empty. When the export has no client ID, every session has the same hash. FK to [Users](#mart-users) |
| `geoHash` | STRING | Geo Hash | FARM\_FINGERPRINT of the session's country, region and city. FK to [Geo](#mart-geo) |
| `deviceHash` | STRING | Device Hash | FARM\_FINGERPRINT of the session's device category, browser and version, operating system and version. FK to [Devices](#mart-devices) |
| `trafficSourceHash` | STRING | Traffic Source Hash | FARM\_FINGERPRINT of the session's source, medium, campaign, keyword and ad content. FK to [Traffic Sources](#mart-traffic-sources) |
| `transactions` | INTEGER | Purchases | Purchases in the session, deduplicated by transaction ID the same way as in Transactions. 0 when none. |
| `transactionRevenue` | FLOAT | Purchase Revenue (millionths) | Revenue of those purchases, multiplied by 1,000,000 as Google's export stores it; divide by 1,000,000 for the amount. 0 when none. |
| `impressions` | INTEGER | Impressions | Always 0. A slot for ad impressions blended in later. |
| `clicks` | INTEGER | Clicks | Always 0. A slot for ad clicks blended in later. |
| `cost` | FLOAT | Cost | Always 0. A slot for ad cost blended in later. |

### Relationships

| Related data mart | On | Cardinality | Meaning |
| --- | --- | --- | --- |
| [Geo](#mart-geo) | `geoHash = geoHash` | N:1 | Where the session came from; the hash repeats across runs. |
| [Devices](#mart-devices) | `deviceHash = deviceHash` | N:1 | The device the session ran on; the hash repeats across runs. |
| [Traffic Sources](#mart-traffic-sources) | `trafficSourceHash = trafficSourceHash` | N:1 | The session's source and campaign; the hash repeats across runs. |
| [Users](#mart-users) | `userHash = userHash` | N:1 | The browser behind the session, by client ID; one shared hash when the export has none. |

## Traffic Sources

The source, medium, campaign, keyword and ad content Google Analytics attributed a session to, one row per combination. They are read as they are from the export's `trafficSource` record, such as `google` / `organic`, `(direct)` / `(none)`, or the UTM tags of a campaign. Sessions point here through `trafficSourceHash`, a FARM\_FINGERPRINT of the five values. The table has no date, so a hash repeats once per run (once per day for daily runs). Count distinct hashes. In your own SQL over these tables, deduplicate it before joining, or rows multiply; OWOX Data Marts reports collapse joined fields per source row.

### Fields

| Column | Type | Alias | Description |
| --- | --- | --- | --- |
| `trafficSourceHash` | STRING | Traffic Source Hash | PK. FARM\_FINGERPRINT of source, medium, campaign, keyword and ad content, joined without a separator, with an empty value in place of NULL. Unique within one run; repeats across daily runs. |
| `source` | STRING | Source | Where the session came from, from `trafficSource.source`, such as `google` or `(direct)`. |
| `medium` | STRING | Medium | The kind of traffic, from `trafficSource.medium`, such as `organic`, `cpc`, `referral` or `(none)`. |
| `campaign` | STRING | Campaign | The campaign name, from `trafficSource.campaign`. |
| `keyword` | STRING | Keyword | The keyword or search term, from `trafficSource.keyword`. |
| `adContent` | STRING | Ad Content | The ad variant, from `trafficSource.adContent`. |

## Transactions

One row per purchase, deduplicated by transaction ID: when the export records the same ID more than once within the deduplication window (7 days in the runbook's setup), only the earliest is kept, so there is at most one row per ID per deduplication window. `transactionRevenue` is in millionths, as Google's export stores it: divide by 1,000,000. `status` is always empty and `touchpoint` is always `WEB`; both are slots. Join to Sessions on `date` and `sessionId` for the source, device and location behind each order. The purchase counts on Sessions are deduplicated the same way.

### Fields

| Column | Type | Alias | Description |
| --- | --- | --- | --- |
| `date` | DATE | Date | PK. The day of the export table the purchase was read from, in the property's time zone. |
| `sessionId` | STRING | Session ID | PK. The session the purchase was made in: the full visitor ID and the visit ID, joined by `_`. Join to Sessions on this and `date` together. FK to [Sessions](#mart-sessions) |
| `transactionId` | STRING | Transaction ID | PK. The order ID the site sent with the purchase. At most one row per ID per deduplication window: the earliest. |
| `transactionRevenue` | FLOAT | Revenue (millionths) | Revenue of the purchase, multiplied by 1,000,000 as Google's export stores it; divide by 1,000,000 for the amount. |
| `status` | STRING | Status | Always empty. A slot for an order status from another source. |
| `touchpoint` | STRING | Touchpoint | Always `WEB`. |

### Relationships

| Related data mart | On | Cardinality | Meaning |
| --- | --- | --- | --- |
| [Sessions](#mart-sessions) | `date = date, sessionId = sessionId` | N:1 | The session the purchase was made in. |

## Users

The visitors behind sessions, one row per client ID: the identifier Google Analytics gives a browser, not a person. The model builds a user from `clientId` alone; the other identifier columns are always empty, slots for other sources. Where the export carries no `clientId`, as in Google's public sample, every session lands on one user hash and this table holds one row per run. Sessions point here through `userHash`. The table has no date, so a hash repeats once per run (once per day for daily runs). Count distinct hashes. In your own SQL over these tables, deduplicate it before joining, or rows multiply; OWOX Data Marts reports collapse joined fields per source row.

### Fields

| Column | Type | Alias | Description |
| --- | --- | --- | --- |
| `userHash` | STRING | User Hash | PK. FARM\_FINGERPRINT of the six identifiers below, joined without a separator, with an empty value in place of NULL; today the client ID alone. Unique within one run; repeats across daily runs. |
| `clientId` | STRING | Client ID | The export's `clientId`: one browser. Empty in exports that do not carry it, Google's public sample included. |
| `userId` | STRING | User ID | Always empty. A slot for your own user ID. |
| `visitorId` | STRING | Visitor ID | Always empty. A slot for a visitor ID from another source. |
| `userPseudoId` | STRING | Pseudo User ID | Always empty. A slot for a GA4 pseudo user ID. |
| `phone` | STRING | Phone | Always empty. A slot for a phone number from your CRM. |
| `email` | STRING | Email | Always empty. A slot for an email address from your CRM. |

## Apply to your project

1.  1

    ### Install the Import Model plugin

    One plugin, installed once, in your own OWOX workspace.

    [Get the plugin →](https://github.com/OWOX/import-model)

2.  2

    ### Import this model

    Point it at this bundle and it creates every data mart above, joins and all.

    [Open the model →](https://model.owox.com/?okf=https://github.com/OWOX/models/tree/main/bundles/ga360-standard)

3.  3

    ### Plug in your data and destinations

    Connect your own sources and send the results where your team already works.

    [Browse connectors →](/connectors)

**4\. Optional — customize as you wish.** Rename a column, drop a mart, add your own: once it is imported it is yours, and nothing here syncs back.

## Links

Pages this page links to, on this site and on docs.owox.com. Where the page has a Markdown twin, its address follows the link.

- [Browse connectors →](https://www.owox.com/connectors) — /connectors.md
