{"id":10122,"date":"2026-09-27T08:05:00","date_gmt":"2026-09-27T06:05:00","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/?p=10122"},"modified":"2026-10-26T11:41:34","modified_gmt":"2026-10-26T10:41:34","slug":"tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query","status":"publish","type":"post","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/","title":{"rendered":"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query"},"content":{"rendered":"<p>BigQuery makes exported GA4 events available for custom SQL analysis: joins with business data, explicit metric definitions and observed page sequences. GA4 also offers path and funnel explorations; SQL adds control over the calculation and output rather than making all interface analysis obsolete.<\/p>\n<p>This tutorial starts with daily export, checks whether events arrive and then groups observed web page views by session identifiers. The examples describe the selected export, not every real visit or a guaranteed complete journey.<\/p>\n<figure class=\"lw-diagram\"><img decoding=\"async\" src=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/diagrams\/dia-10122-bigquery-20261024.png\" width=\"1120\" height=\"580\" loading=\"lazy\" alt=\"Export scope, stable identifiers and ordering limits determine the meaning of a reconstructed path.\"><figcaption>Export scope, stable identifiers and ordering limits determine the meaning of a reconstructed path.<\/figcaption><\/figure>\n<h2>1. Daily export setup<\/h2>\n<p>In GA4, the link is created under <strong>Admin \u2192 Product links \u2192 BigQuery links<\/strong>. The GA4 account needs Editor or higher access at property level. The Cloud project needs the documented IAM and service permissions; Owner includes those permissions, but a suitable narrower role combination can also provide the required access. The BigQuery API must be enabled.<\/p>\n<p>The setup selects the Cloud project, data location, streams and any excluded events. For standard properties, Daily and Streaming are available independently. Eligible Analytics 360 properties also offer Fresh Daily. Daily produces <code>events_YYYYMMDD<\/code>; Streaming produces provisional <code>events_intraday_YYYYMMDD<\/code> tables and can have gaps. The daily table is preferable for completed-day analysis, although late events can update recent daily tables.<\/p>\n<p>The link does not backfill the property&#8217;s previous history. Initial data normally starts arriving within 24 hours; a particular daily delivery time is not guaranteed. Standard properties have a daily limit of one million exported events. Exceeding the limit can suspend daily export; stream and event exclusions can reduce volume but also change the scope of subsequent analysis.<\/p>\n<p>Daily export does not make Cloud usage free: storage and query processing can incur charges. Streaming adds ingestion costs. Before the first query, the editor&#8217;s estimate or a dry run, which checks the query without running it, shows expected processing; <code>maximum bytes billed<\/code> provides a per-query limit under on-demand pricing. The examples below use a deliberately bounded date range.<\/p>\n<h2>2. Differences from GA4 reports<\/h2>\n<p>The event export is unsampled and does not collapse high-cardinality values into an <code>(other)<\/code> row. GA4 reports and explorations can use different source tables and may involve sampling, aggregation limits or privacy thresholds. Thresholds are relevant, for example, to demographic and search-query information; enabling Google signals is not a universal explanation for every missing row.<\/p>\n<p>BigQuery does not reproduce GA4&#8217;s behavioral or key-event modeling or data-driven attribution. This does not mean that all attribution fields are absent: the current schema includes observed last-click session traffic-source fields where available. Differences should be investigated by matching the metric, date range, property timezone, identity method, export filters and data freshness. A discrepancy is not automatically a fault, but also not automatically harmless.<\/p>\n<h2>3. The nested schema<\/h2>\n<p>Each exported row represents an event. Top-level fields include <code>event_name<\/code>, <code>event_date<\/code>, <code>event_timestamp<\/code> and <code>user_pseudo_id<\/code>. Additional values such as <code>ga_session_id<\/code> and <code>page_location<\/code> sit in <code>event_params<\/code>, a list of name\/value pairs within the event row. <code>UNNEST<\/code> exposes those records; a scalar subquery, which returns a single value, retrieves the value for one key.<\/p>\n<p><code>event_date<\/code> follows the configured reporting timezone. <code>event_timestamp<\/code> is measured in microseconds UTC, but several batched events can share a timestamp. Batch fields add ordering information. A pseudo-ID identifies an app instance or browser context, not a verified person; missing or changing identifiers limit reconstruction. The query keeps <code>stream_id<\/code>, pseudo-ID and session ID as separate grouping fields.<\/p>\n<h2>4. Exported event availability<\/h2>\n<p>The project ID, property dataset and dates below are placeholders to replace with existing daily tables. GoogleSQL is required.<\/p>\n<figure style=\"--cbb-digits:1\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"sql\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">SQL<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-sql\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-keyword\">SELECT<\/span>\n<\/span><span class=\"cbb-line\">  event_date,\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-function\">COUNT<\/span>(*) <span class=\"cbb-keyword\">AS<\/span> events,\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-function\">COUNT<\/span>(<span class=\"cbb-keyword\">DISTINCT<\/span> user_pseudo_id) <span class=\"cbb-keyword\">AS<\/span> distinct_pseudo_ids,\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-function\">COUNTIF<\/span>(user_pseudo_id <span class=\"cbb-keyword\">IS<\/span> <span class=\"cbb-constant\">NULL<\/span>) <span class=\"cbb-keyword\">AS<\/span> events_without_pseudo_id\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">FROM<\/span> <span class=\"cbb-variable\">`project.analytics_XXXXXXXXX.events_*`<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">WHERE<\/span> _TABLE_SUFFIX <span class=\"cbb-keyword\">BETWEEN<\/span> <span class=\"cbb-string\">'20260920'<\/span> <span class=\"cbb-keyword\">AND<\/span> <span class=\"cbb-string\">'20260926'<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">GROUP<\/span> <span class=\"cbb-keyword\">BY<\/span> event_date\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">ORDER<\/span> <span class=\"cbb-keyword\">BY<\/span> event_date;<\/span><\/code><\/pre><\/figure>\n<p><code>distinct_pseudo_ids<\/code> counts distinct non-null identifiers, not GA4 Active users or unique people. The extra null count makes missing identifiers visible. A day without a table or without events produces no result row; a continuous calendar would need a separate date table.<\/p>\n<h2>5. An observed page sequence<\/h2>\n<p>This web example uses <code>page_view<\/code>; app screen analysis needs a different event and location field. The selected tables must contain the batch fields shown in the current schema.<\/p>\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"sql\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">SQL<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-sql\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-keyword\">WITH<\/span> page_views <span class=\"cbb-keyword\">AS<\/span> (\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-keyword\">SELECT<\/span>\n<\/span><span class=\"cbb-line\">    stream_id,\n<\/span><span class=\"cbb-line\">    user_pseudo_id,\n<\/span><span class=\"cbb-line\">    (<span class=\"cbb-keyword\">SELECT<\/span> value.int_value <span class=\"cbb-keyword\">FROM<\/span> <span class=\"cbb-keyword\">UNNEST<\/span>(event_params)\n<\/span><span class=\"cbb-line\">      <span class=\"cbb-keyword\">WHERE<\/span> <span class=\"cbb-keyword\">key<\/span> = <span class=\"cbb-string\">'ga_session_id'<\/span>) <span class=\"cbb-keyword\">AS<\/span> session_id,\n<\/span><span class=\"cbb-line\">    event_timestamp,\n<\/span><span class=\"cbb-line\">    batch_page_id,\n<\/span><span class=\"cbb-line\">    batch_ordering_id,\n<\/span><span class=\"cbb-line\">    batch_event_index,\n<\/span><span class=\"cbb-line\">    (<span class=\"cbb-keyword\">SELECT<\/span> value.string_value <span class=\"cbb-keyword\">FROM<\/span> <span class=\"cbb-keyword\">UNNEST<\/span>(event_params)\n<\/span><span class=\"cbb-line\">      <span class=\"cbb-keyword\">WHERE<\/span> <span class=\"cbb-keyword\">key<\/span> = <span class=\"cbb-string\">'page_location'<\/span>) <span class=\"cbb-keyword\">AS<\/span> page_location\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-keyword\">FROM<\/span> <span class=\"cbb-variable\">`project.analytics_XXXXXXXXX.events_*`<\/span>\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-keyword\">WHERE<\/span> _TABLE_SUFFIX <span class=\"cbb-keyword\">BETWEEN<\/span> <span class=\"cbb-string\">'20260920'<\/span> <span class=\"cbb-keyword\">AND<\/span> <span class=\"cbb-string\">'20260926'<\/span>\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">AND<\/span> event_name = <span class=\"cbb-string\">'page_view'<\/span>\n<\/span><span class=\"cbb-line\">)\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">SELECT<\/span>\n<\/span><span class=\"cbb-line\">  stream_id,\n<\/span><span class=\"cbb-line\">  user_pseudo_id,\n<\/span><span class=\"cbb-line\">  session_id,\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-function\">COUNT<\/span>(*) <span class=\"cbb-keyword\">AS<\/span> page_views,\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-function\">COUNTIF<\/span>(page_location <span class=\"cbb-keyword\">IS<\/span> <span class=\"cbb-constant\">NULL<\/span>) <span class=\"cbb-keyword\">AS<\/span> missing_locations,\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-function\">STRING_AGG<\/span>(\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-function\">COALESCE<\/span>(<span class=\"cbb-function\">REGEXP_REPLACE<\/span>(page_location, r<span class=\"cbb-string\">'&#91;?#&#93;.*$'<\/span>, <span class=\"cbb-string\">''<\/span>),\n<\/span><span class=\"cbb-line\">             <span class=\"cbb-string\">'&#91;missing URL&#93;'<\/span>),\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-string\">' &gt; '<\/span> <span class=\"cbb-keyword\">ORDER<\/span> <span class=\"cbb-keyword\">BY<\/span> event_timestamp, batch_page_id,\n<\/span><span class=\"cbb-line\">      batch_ordering_id, batch_event_index, page_location\n<\/span><span class=\"cbb-line\">  ) <span class=\"cbb-keyword\">AS<\/span> observed_path\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">FROM<\/span> page_views\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">WHERE<\/span> user_pseudo_id <span class=\"cbb-keyword\">IS<\/span> <span class=\"cbb-keyword\">NOT<\/span> <span class=\"cbb-constant\">NULL<\/span>\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-keyword\">AND<\/span> session_id <span class=\"cbb-keyword\">IS<\/span> <span class=\"cbb-keyword\">NOT<\/span> <span class=\"cbb-constant\">NULL<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">GROUP<\/span> <span class=\"cbb-keyword\">BY<\/span> stream_id, user_pseudo_id, session_id\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">HAVING<\/span> <span class=\"cbb-function\">COUNT<\/span>(*) &gt; <span class=\"cbb-number\">1<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">ORDER<\/span> <span class=\"cbb-keyword\">BY<\/span> page_views <span class=\"cbb-keyword\">DESC<\/span>, stream_id, user_pseudo_id, session_id\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">LIMIT<\/span> <span class=\"cbb-number\">100<\/span>;<\/span><\/code><\/pre><\/figure>\n<p>Missing pseudo-IDs and session IDs are excluded instead of merging unrelated events into a shared null group. Missing locations remain visible in both the count and the path. The example removes query strings and fragments while preserving the hostname, so pages on different hosts do not silently collapse into one label. Query-based routing needs a different normalization rule.<\/p>\n<p>The ordering uses timestamps and available batch fields; the final URL field provides a display tie-breaker, not evidence of chronological order. Equal or incomplete ordering values, parallel tabs and delayed delivery can leave the real sequence ambiguous. The path contains only observed page views inside the selected dates. Sessions crossing the range boundary can be truncated, and single-page sessions are deliberately omitted by <code>HAVING<\/code>.<\/p>\n<p>The scalar subqueries expect at most one value for each requested parameter key in an event. Duplicate keys should be investigated if the query fails; choosing an arbitrary first value would hide the data problem. Purchase-step analysis additionally needs purchase events: the current filter retains only page views.<\/p>\n<h2>6. Result checks before dashboard use<\/h2>\n<ul><li>Checks cover daily event counts, excluded streams\/events and the proportion of missing identifiers.<\/li><li>Several reconstructed sessions are compared with the underlying rows, including equal timestamps and sessions crossing midnight.<\/li><li>An explicitly defined metric is compared with GA4 under the same date and identity assumptions. Expected modeling differences are distinguished from export gaps.<\/li><li>Dataset access and table expiration require deliberate configuration. Exported data has a separate lifecycle from GA4.<\/li><\/ul>\n<div class=\"lw-faq\"><h2>Questions and answers<\/h2>\n<h3>Does events_* include streaming tables?<\/h3><p>Yes. The numeric suffix range in both examples excludes <code>intraday_YYYYMMDD<\/code>. A query including recent streaming data should select only one table source per date; overlapping daily and intraday rows must not be added together. Streaming is provisional and is not a completeness guarantee.<\/p>\n<h3>Does LIMIT 100 control query cost?<\/h3><p>Not for these aggregate queries over ordinary unclustered export tables. The limit restricts the returned sessions after aggregation. Selected columns, bounded table suffixes, a dry run and maximum bytes billed are the relevant controls. BigQuery also supports capacity pricing, so not every deployment is billed directly per scanned byte.<\/p>\n<h3>Does GA4 retention delete BigQuery tables?<\/h3><p>No. BigQuery needs its own expiration and deletion policy. Changing the dataset&#8217;s default table expiration affects newly created tables; existing tables need their own expiration settings. An expiration deadline is based on table creation, not the individual event&#8217;s occurrence time.<\/p>\n<h3>Can export run without a billing account?<\/h3><p>Daily export can use the BigQuery sandbox. Current sandbox documentation specifies a lifetime 10 GiB storage quota that deletion does not restore, 1 TiB of query processing per month and automatic expiration after 60 days. Streaming is unavailable. Upgrading removes sandbox restrictions but requires a separate cost and retention plan.<\/p><\/div><div class=\"lw-quellen\"><h2>Sources<\/h2><ul><li><a href=\"https:\/\/support.google.com\/analytics\/answer\/9823238?hl=en\" target=\"_blank\" rel=\"noopener noreferrer\">Google Analytics: Set up BigQuery Export<\/a><\/li><li><a href=\"https:\/\/support.google.com\/analytics\/answer\/9358801?hl=en\" target=\"_blank\" rel=\"noopener noreferrer\">Google Analytics: BigQuery Export<\/a><\/li><li><a href=\"https:\/\/support.google.com\/analytics\/answer\/7029846?hl=en\" target=\"_blank\" rel=\"noopener noreferrer\">Google Analytics: BigQuery Export schema<\/a><\/li><li><a href=\"https:\/\/support.google.com\/analytics\/answer\/13644080?hl=en\" target=\"_blank\" rel=\"noopener noreferrer\">Google Analytics: Reporting surfaces comparison<\/a><\/li><li><a href=\"https:\/\/support.google.com\/analytics\/answer\/9383630?hl=en\" target=\"_blank\" rel=\"noopener noreferrer\">Google Analytics: About data thresholds<\/a><\/li><li><a href=\"https:\/\/docs.cloud.google.com\/bigquery\/docs\/sandbox\" target=\"_blank\" rel=\"noopener noreferrer\">Google Cloud: Try BigQuery using the sandbox<\/a><\/li><li><a href=\"https:\/\/docs.cloud.google.com\/bigquery\/docs\/best-practices-costs\" target=\"_blank\" rel=\"noopener noreferrer\">Google Cloud: Estimate and control costs<\/a><\/li><li><a href=\"https:\/\/docs.cloud.google.com\/bigquery\/docs\/updating-datasets\" target=\"_blank\" rel=\"noopener noreferrer\">Google Cloud: Update dataset properties<\/a><\/li><li><a href=\"https:\/\/developers.google.com\/analytics\/devguides\/collection\/ga4\" target=\"_blank\" rel=\"noopener noreferrer\">Google Analytics 4 developer guide<\/a><\/li><\/ul><\/div>","protected":false},"excerpt":{"rendered":"<p>Setting up daily GA4 export, checking received events and querying observed page sequences in BigQuery. Missing identifiers and ambiguous event order limit reconstruction; storage and queries can incur costs.<\/p>\n","protected":false},"author":1,"featured_media":29050,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[113],"tags":[91333,91258,91407,91219,91252],"class_list":["post-10122","post","type-post","status-publish","format-standard","hentry","category-tutorials","tag-bigquery","tag-google-analytics-4","tag-google-cloud","tag-tutorial","tag-web-analytics"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query | Lukas Wojcik<\/title>\n<meta name=\"description\" content=\"Enabling the free daily GA4 export to BigQuery, seeing what the interface does to the numbers, and a first query on the nested event schema.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query | Lukas Wojcik\" \/>\n<meta property=\"og:description\" content=\"Enabling the free daily GA4 export to BigQuery, seeing what the interface does to the numbers, and a first query on the nested event schema.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-27T06:05:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-26T10:41:34+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/11\/flux-voll-10122-10122.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Lukas Wojcik\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Lukas Wojcik\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/\"},\"author\":{\"name\":\"Lukas Wojcik\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"headline\":\"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query\",\"datePublished\":\"2026-09-27T06:05:00+00:00\",\"dateModified\":\"2026-10-26T10:41:34+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/\"},\"wordCount\":1145,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/flux-voll-10122-10122.png\",\"keywords\":[\"BigQuery\",\"Google Analytics 4\",\"Google Cloud\",\"Tutorial\",\"Web Analytics\"],\"articleSection\":[\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/\",\"name\":\"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query | Lukas Wojcik\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/flux-voll-10122-10122.png\",\"datePublished\":\"2026-09-27T06:05:00+00:00\",\"dateModified\":\"2026-10-26T10:41:34+00:00\",\"description\":\"Enabling the free daily GA4 export to BigQuery, seeing what the interface does to the numbers, and a first query on the nested event schema.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/flux-voll-10122-10122.png\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/flux-voll-10122-10122.png\",\"width\":1200,\"height\":630,\"caption\":\"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"Lukas Wojcik\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"Lukas Wojcik\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query | Lukas Wojcik","description":"Enabling the free daily GA4 export to BigQuery, seeing what the interface does to the numbers, and a first query on the nested event schema.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/","og_locale":"en_US","og_type":"article","og_title":"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query | Lukas Wojcik","og_description":"Enabling the free daily GA4 export to BigQuery, seeing what the interface does to the numbers, and a first query on the nested event schema.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/","og_site_name":"Lukas Wojcik - Blog","article_published_time":"2026-09-27T06:05:00+00:00","article_modified_time":"2026-10-26T10:41:34+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/11\/flux-voll-10122-10122.png","type":"image\/png"}],"author":"Lukas Wojcik","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Lukas Wojcik","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#article","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/"},"author":{"name":"Lukas Wojcik","@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"headline":"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query","datePublished":"2026-09-27T06:05:00+00:00","dateModified":"2026-10-26T10:41:34+00:00","mainEntityOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/"},"wordCount":1145,"commentCount":0,"publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/11\/flux-voll-10122-10122.png","keywords":["BigQuery","Google Analytics 4","Google Cloud","Tutorial","Web Analytics"],"articleSection":["Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/","name":"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query | Lukas Wojcik","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#primaryimage"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/11\/flux-voll-10122-10122.png","datePublished":"2026-09-27T06:05:00+00:00","dateModified":"2026-10-26T10:41:34+00:00","description":"Enabling the free daily GA4 export to BigQuery, seeing what the interface does to the numbers, and a first query on the nested event schema.","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#primaryimage","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/11\/flux-voll-10122-10122.png","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/11\/flux-voll-10122-10122.png","width":1200,"height":630,"caption":"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query"},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"Lukas Wojcik","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"Lukas Wojcik"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/10122","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=10122"}],"version-history":[{"count":7,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/10122\/revisions"}],"predecessor-version":[{"id":26628,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/10122\/revisions\/26628"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media\/29050"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=10122"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/categories?post=10122"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=10122"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}