{"id":12890,"date":"2026-09-05T06:30:00","date_gmt":"2026-09-05T04:30:00","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/?p=12890"},"modified":"2026-10-24T04:15:29","modified_gmt":"2026-10-24T02:15:29","slug":"reading-the-per-event-consent-state-from-the-ga4-bigquery-export","status":"publish","type":"post","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/","title":{"rendered":"Reading the Per-Event Consent State From the GA4 BigQuery Export"},"content":{"rendered":"<p>In the GA4 BigQuery export, <code>privacy_info<\/code> contains storage-state information for an exported event. This supports technical checks of consent signals. It does not replace CMP records or establish a particular choice made on a banner.<\/p>\n<p>The common SQL mistake: <code>analytics_storage != 'Yes'<\/code> excludes NULL values. A comparison with NULL does not produce true; <code>WHERE<\/code> retains only true conditions.<\/p>\n<h2>1. Inspect the raw values first<\/h2>\n<p><code>analytics_storage<\/code> and <code>ads_storage<\/code> are strings. Google documents <code>Yes<\/code>, <code>No<\/code> and <code>Unset<\/code>. An actual SQL NULL remains separately identifiable as a missing value. <code>'Unset'<\/code> and NULL are not interchangeable in comparisons. Dot notation accesses each field within the record.<\/p>\n<p>Replace the project, dataset and example dates before execution. Grouping first exposes all existing combinations without classifying unknown values as refusals.<\/p>\n<pre><code class=\"language-sql\">SELECT\n  event_date,\n  privacy_info.analytics_storage AS analytics_storage,\n  privacy_info.ads_storage AS ads_storage,\n  COUNT(*) AS event_count\nFROM `project_id.analytics_XXXXXX.events_*`\nWHERE _TABLE_SUFFIX BETWEEN &#x27;20260801&#x27; AND &#x27;20260831&#x27;\nGROUP BY 1, 2, 3\nORDER BY event_date, event_count DESC;\n<\/code><\/pre>\n<figure class=\"lw-diagram\"><img decoding=\"async\" src=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/diagrams\/dia-12890-consent-sql-20261024-en.png\" alt=\"Distinguish Yes, No, Unset and SQL NULL. Only true comparisons pass a WHERE filter.\" width=\"1120\" height=\"580\" loading=\"lazy\"><figcaption>Distinguish Yes, No, Unset and SQL NULL. Only true comparisons pass a WHERE filter.<\/figcaption><\/figure>\n<h2>2. Demonstrate the NULL trap with four values<\/h2>\n<p>This small GoogleSQL example needs no Analytics data. It distinguishes explicit <code>No<\/code>, not <code>Yes<\/code>, and not <code>Yes<\/code> including NULL.<\/p>\n<pre><code class=\"language-sql\">WITH sample AS (\n  SELECT state\n  FROM UNNEST(ARRAY&lt;STRING&gt;[&#x27;Yes&#x27;, &#x27;No&#x27;, &#x27;Unset&#x27;, NULL]) AS state\n)\nSELECT state,\n  state = &#x27;No&#x27; AS equals_no,\n  state != &#x27;Yes&#x27; AS not_yes,\n  state IS DISTINCT FROM &#x27;Yes&#x27; AS not_yes_including_null\nFROM sample;\n<\/code><\/pre>\n<p>For <code>No<\/code> and <code>Unset<\/code>, <code>!= 'Yes'<\/code> is true. For NULL, the result is NULL, so a matching <code>WHERE<\/code> filter removes the row. <code>IS DISTINCT FROM 'Yes'<\/code> includes NULL. Its technical meaning is \u201cnot Yes, including missing,\u201d not \u201cexplicit refusal.\u201d<\/p>\n<p>Even <code>No<\/code> does not establish a click on \u201cReject\u201d: a denied storage default can apply before a banner decision. The field alone cannot explain the cause of unknown values.<\/p>\n<h2>3. Make the denominator explicit<\/h2>\n<p>The next query separates known, unset, missing and unexpected values. <code>SAFE_DIVIDE<\/code> returns NULL for a zero denominator instead of failing. The shares range from 0 to 1; percentage display needs suitable formatting.<\/p>\n<pre><code class=\"language-sql\">WITH source AS (\n  SELECT privacy_info.analytics_storage AS state\n  FROM `project_id.analytics_XXXXXX.events_*`\n  WHERE _TABLE_SUFFIX BETWEEN &#x27;20260801&#x27; AND &#x27;20260831&#x27;\n)\nSELECT\n  COUNT(*) AS exported_events,\n  COUNTIF(state = &#x27;Yes&#x27;) AS yes_events,\n  COUNTIF(state = &#x27;No&#x27;) AS no_events,\n  COUNTIF(state = &#x27;Unset&#x27;) AS unset_events,\n  COUNTIF(state IS NULL) AS null_events,\n  COUNTIF(state NOT IN (&#x27;Yes&#x27;, &#x27;No&#x27;, &#x27;Unset&#x27;)) AS other_events,\n  SAFE_DIVIDE(COUNTIF(state = &#x27;Yes&#x27;), COUNT(*)) AS yes_share_all_events,\n  SAFE_DIVIDE(\n    COUNTIF(state = &#x27;Yes&#x27;),\n    COUNTIF(state IN (&#x27;Yes&#x27;, &#x27;No&#x27;))\n  ) AS yes_share_known_events\nFROM source;\n<\/code><\/pre>\n<p>With one event each for <code>Yes<\/code>, <code>No<\/code>, <code>Unset<\/code> and NULL, the Yes share of all events is 25%. Among the two known states, it is 50%. Both calculations can be correct while answering different questions. Unknown-value shares must remain visible alongside them.<\/p>\n<p>Both results are <strong>event-weighted<\/strong>: a visit producing many events contributes more than a visit producing few. Under basic consent mode, visits without permission can be absent from the export entirely. A banner-decision rate therefore needs a suitably defined CMP dataset, including period, counting unit and treatment of repeated decisions.<\/p>\n<h2>4. Investigate patterns<\/h2>\n<ul>\n<li><strong>Page and event:<\/strong> Are unknown states concentrated on a template or an exclusively server-generated event?<\/li>\n<li><strong>Time:<\/strong> Does a change coincide with a release or CMP adjustment?<\/li>\n<li><strong>Device and source:<\/strong> Does a difference persist when event mix and data availability are comparable?<\/li>\n<\/ul>\n<p>These patterns guide investigation; they do not prove a cause. Tag configuration, consent initialization, network requests and relevant server-side forwarding must corroborate the finding.<\/p>\n<p>A visit can pass through multiple storage states. A stable pseudonymous identifier across denied and granted events is not guaranteed. Without suitable identifiers, earlier events cannot reliably be reconstructed into a session; joining missing identifiers does not solve that problem.<\/p>\n<div class=\"lw-faq\">\n<h2>Questions and answers<\/h2>\n<h3>Why _TABLE_SUFFIX rather than event_date alone?<\/h3>\n<p>For these wildcard daily tables, a constant suffix range restricts the selected tables. Date strings exclude <code>events_intraday_\u2026<\/code>. An <code>event_date<\/code>-only filter does not replace this table selection. Check estimated scan volume and cost beforehand; a month is not automatically cheap.<\/p>\n<h3>Does COALESCE explain missing values?<\/h3>\n<p>No. <code>COALESCE(state, 'missing')<\/code> merely gives NULL a visible label. It does not establish whether the cause was missing configuration, a different collection path or something else.<\/p>\n<h3>Why name columns explicitly?<\/h3>\n<p>Named columns limit dependency on the export schema. New fields do not automatically change the output shape. Changes to required field types or missing fields can still require adjustments.<\/p>\n<\/div>\n<h2>Sources<\/h2>\n<ul>\n<li><a href=\"https:\/\/support.google.com\/analytics\/answer\/7029846\">GA4 export schema: privacy_info<\/a><\/li>\n<li><a href=\"https:\/\/docs.cloud.google.com\/bigquery\/docs\/reference\/standard-sql\/operators\">GoogleSQL comparisons and NULL<\/a><\/li>\n<li><a href=\"https:\/\/docs.cloud.google.com\/bigquery\/docs\/querying-wildcard-tables\">Constant suffix filters<\/a><\/li>\n<li><a href=\"https:\/\/developers.google.com\/tag-platform\/security\/concepts\/consent-mode\">Basic and advanced consent mode<\/a><\/li>\n<li><a href=\"https:\/\/support.google.com\/analytics\/answer\/11161109?hl=en\">Identifier limits without consent<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>GA4 BigQuery exports contain event-level consent fields. SQL examples separate Yes, No, Unset and SQL NULL, define the denominator and explain why an event-based share is not a visitor consent rate.<\/p>\n","protected":false},"author":1,"featured_media":18795,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[91333,91041,91258],"class_list":["post-12890","post","type-post","status-publish","format-standard","hentry","category-digital-analytics","tag-bigquery","tag-consent-mode-v2","tag-google-analytics-4"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Reading the Per-Event Consent State From the GA4 BigQuery Export | Lukas Wojcik<\/title>\n<meta name=\"description\" content=\"How to read analytics_storage and ads_storage per event in the GA4 BigQuery export, why the null case breaks a consent rate, and what the raw export shows that the reports do not.\" \/>\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\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Reading the Per-Event Consent State From the GA4 BigQuery Export | Lukas Wojcik\" \/>\n<meta property=\"og:description\" content=\"How to read analytics_storage and ads_storage per event in the GA4 BigQuery export, why the null case breaks a consent rate, and what the raw export shows that the reports do not.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-05T04:30:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-24T02:15:29+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-12890-reading-the-per-event-consent-state--k.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=\"4 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\\\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\\\/\"},\"author\":{\"name\":\"Lukas Wojcik\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"headline\":\"Reading the Per-Event Consent State From the GA4 BigQuery Export\",\"datePublished\":\"2026-09-05T04:30:00+00:00\",\"dateModified\":\"2026-10-24T02:15:29+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\\\/\"},\"wordCount\":575,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/hero-12890-reading-the-per-event-consent-state--k.png\",\"keywords\":[\"BigQuery\",\"Consent Mode v2\",\"Google Analytics 4\"],\"articleSection\":[\"Digital Analytics\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\\\/\",\"name\":\"Reading the Per-Event Consent State From the GA4 BigQuery Export | Lukas Wojcik\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/hero-12890-reading-the-per-event-consent-state--k.png\",\"datePublished\":\"2026-09-05T04:30:00+00:00\",\"dateModified\":\"2026-10-24T02:15:29+00:00\",\"description\":\"How to read analytics_storage and ads_storage per event in the GA4 BigQuery export, why the null case breaks a consent rate, and what the raw export shows that the reports do not.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/hero-12890-reading-the-per-event-consent-state--k.png\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/hero-12890-reading-the-per-event-consent-state--k.png\",\"width\":1200,\"height\":630,\"caption\":\"Reading the Per-Event Consent State From the GA4 BigQuery Export\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Reading the Per-Event Consent State From the GA4 BigQuery Export\"}]},{\"@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":"Reading the Per-Event Consent State From the GA4 BigQuery Export | Lukas Wojcik","description":"How to read analytics_storage and ads_storage per event in the GA4 BigQuery export, why the null case breaks a consent rate, and what the raw export shows that the reports do not.","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\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/","og_locale":"en_US","og_type":"article","og_title":"Reading the Per-Event Consent State From the GA4 BigQuery Export | Lukas Wojcik","og_description":"How to read analytics_storage and ads_storage per event in the GA4 BigQuery export, why the null case breaks a consent rate, and what the raw export shows that the reports do not.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/","og_site_name":"Lukas Wojcik - Blog","article_published_time":"2026-09-05T04:30:00+00:00","article_modified_time":"2026-10-24T02:15:29+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-12890-reading-the-per-event-consent-state--k.png","type":"image\/png"}],"author":"Lukas Wojcik","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Lukas Wojcik","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/#article","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/"},"author":{"name":"Lukas Wojcik","@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"headline":"Reading the Per-Event Consent State From the GA4 BigQuery Export","datePublished":"2026-09-05T04:30:00+00:00","dateModified":"2026-10-24T02:15:29+00:00","mainEntityOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/"},"wordCount":575,"commentCount":0,"publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-12890-reading-the-per-event-consent-state--k.png","keywords":["BigQuery","Consent Mode v2","Google Analytics 4"],"articleSection":["Digital Analytics"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/","name":"Reading the Per-Event Consent State From the GA4 BigQuery Export | Lukas Wojcik","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/#primaryimage"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-12890-reading-the-per-event-consent-state--k.png","datePublished":"2026-09-05T04:30:00+00:00","dateModified":"2026-10-24T02:15:29+00:00","description":"How to read analytics_storage and ads_storage per event in the GA4 BigQuery export, why the null case breaks a consent rate, and what the raw export shows that the reports do not.","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/#primaryimage","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-12890-reading-the-per-event-consent-state--k.png","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-12890-reading-the-per-event-consent-state--k.png","width":1200,"height":630,"caption":"Reading the Per-Event Consent State From the GA4 BigQuery Export"},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/reading-the-per-event-consent-state-from-the-ga4-bigquery-export\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Reading the Per-Event Consent State From the GA4 BigQuery Export"}]},{"@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\/12890","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=12890"}],"version-history":[{"count":6,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/12890\/revisions"}],"predecessor-version":[{"id":23535,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/12890\/revisions\/23535"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media\/18795"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=12890"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/categories?post=12890"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=12890"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}