> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omni.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Count GA4 events exactly, including today

> Use a SQL view that reads intraday GA4 tables only for days with no daily export yet, so no day is counted twice.

[All guides](/guides.md)

Progress

%

Category

modeling

Last updated

October 2026

# Count GA4 events exactly, including today

Omni models a Google Analytics 4 dataset's `events_YYYYMMDD` tables as one `events_all` view over `events_*`. Its **Table Date** field (`table_suffix_date`) dates each row by its table's name.

With [streaming export](https://support.google.com/analytics/answer/9358801) on, GA4 writes the current day's events to `events_intraday_YYYYMMDD`, and deletes that table after the day's `events_YYYYMMDD` table is complete. `events_*` matches both, so while both exist, `events_all` counts that day twice. The day is usually yesterday, so reports on finished days are affected too.

If a short double count on the newest finished day doesn't matter for your reporting, use `events_all`. Otherwise, add the view in this guide.

## Requirements

To follow the steps in this guide, you'll need:

* **A BigQuery connection** to a GA4 dataset with both daily and streaming export turned on, and at least one `events_YYYYMMDD` table
* **Modeler** or **Connection Admin** permissions for the connection, so you can edit the shared model

## See the double count

In this example dataset, October 20 has a 14-event daily table, and its 14-event intraday table isn't deleted yet, so `events_all` reports 28:

![A workbook query on Events All with Table Date and Events All Count. October 20, 2026 shows 28 events, double its 14 actual events.](https://mintcdn.com/omni-e7402367/YpXmh6VLcQPc2AoU/guides/modeling/images/ga4-exact-counts-events-all.png?fit=max\&auto=format\&n=YpXmh6VLcQPc2AoU\&q=85\&s=784d323567055ab2b2def4f0616273ac)

## Add an events\_exact view

The new view keeps every daily table. It keeps an intraday table only when its date is later than the newest daily table, because before that, the daily table already holds the same events.

1. In the model IDE, click **File > New view**, name the view `events_exact`, and replace its contents with:

   ```yaml title="events_exact.view" theme={null}
   sql: |
     SELECT *, _TABLE_SUFFIX AS table_suffix
     FROM `my-project.analytics_123456789.events_*`
     WHERE NOT STARTS_WITH(_TABLE_SUFFIX, 'intraday_')
        OR RIGHT(_TABLE_SUFFIX, 8) > (
          SELECT RIGHT(MAX(table_name), 8)
          FROM `my-project.analytics_123456789`.INFORMATION_SCHEMA.TABLES
          WHERE REGEXP_CONTAINS(table_name, r'^events_\d{8}$'))

   dimensions:
     table_suffix:
       hidden: true
     table_date:
       sql: PARSE_DATE('%Y%m%d', RIGHT(${events_exact.table_suffix}, 8))
     event_name: {}
     user_pseudo_id: {}

   measures:
     count:
       aggregate_type: count
   ```

2. Replace `my-project.analytics_123456789` with your BigQuery project and GA4 dataset in both places.

3. Add the other GA4 columns you report on as dimensions, in the same form as `event_name: {}`.

`table_date` uses the last eight characters of `_TABLE_SUFFIX`, so the daily table `20261020` and the intraday table `intraday_20261020` both become October 20, 2026.

To check the result, open a workbook on the model and query **Table Date** and **Count** from **Events Exact**. Compare the most recent daily day with the same day in **Events All**. In this example, `events_exact` counts October 20 once, and still includes October 21, which only has an intraday table:

![A workbook query on Events Exact with Table Date and Events Exact Count. October 20, 2026 shows 14 events and October 21, 2026 shows 8.](https://mintcdn.com/omni-e7402367/YpXmh6VLcQPc2AoU/guides/modeling/images/ga4-exact-counts-events-exact.png?fit=max\&auto=format\&n=YpXmh6VLcQPc2AoU\&q=85\&s=8859b89385020d0cfa0dff3eeaeb281d)

If you transform GA4 data with dbt, Velir's [dbt-ga4](https://github.com/Velir/dbt-ga4) package removes the duplicates in its [`base_ga4__events`](https://github.com/Velir/dbt-ga4/blob/main/models/staging/base/base_ga4__events.sql) model instead.

## Query cost

* **Filter on `table_date`.** BigQuery then reads only the tables in the filter, as it does for the generated **Table Date** field. A filter on another date column, such as `event_date`, reads every table.
* **Every query adds at least 10 MB to the bill.** BigQuery bills [`INFORMATION_SCHEMA` queries at a 10 MB minimum](https://cloud.google.com/bigquery/docs/information-schema-intro#pricing), and the subquery runs with every query on this view. On a small property, this minimum can be most of a query's cost.
* **During the overlap, you pay to read both tables for that day.** BigQuery [skips tables only for filters with fixed values](https://cloud.google.com/bigquery/docs/querying-wildcard-tables#filter_selected_tables_using_table_suffix), and the subquery's result isn't fixed. Until GA4 deletes the intraday table, a query that includes that day reads both tables, then drops the intraday rows.

## Next steps

* [Add the view to a topic](/modeling/topics) so users can find it in workbooks and AI chat.
* [Read about wildcard `table_name`](/modeling/views/parameters/table-name) to combine other sets of BigQuery tables the same way.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.