LW IT Solutions logo LW IT Solutions logo LW IT Solutions
« Blog-Übersicht /Digital Analytics / Einwilligungsstatus je Ereignis im GA4-BigQuery-Export auslesen
Diesen Artikel in anderen Sprachen lesen:

Einwilligungsstatus je Ereignis im GA4-BigQuery-Export auslesen

Einwilligungsstatus je Ereignis im GA4-BigQuery-Export auslesen
Inhalt
  1. Wo er steht
  2. Die Falle
  3. Was damit möglich wird
  4. Zwei Dinge, die über die Zeile zu wissen sind
  5. Fragen und Antworten
  6. Quellen

In GA4-Berichten erscheint die Einwilligung als Zusammenfassung, wenn überhaupt. Im BigQuery-Export wird der Status für jedes Ereignis in einem verschachtelten Datensatz neben den Ereignisdaten festgehalten. Er beschreibt den Zustand zum Zeitpunkt der Aufzeichnung.

Das ist ein deutlich besserer Gegenstand zum Arbeiten, und er bringt eine Falle mit, die eine falsche Zahl erzeugt statt eines Fehlers.

Ein eingerückter Schemabaum der GA4-Ereignistabelle mit hervorgehobenem Satz privacy_info und seinen drei Zeichenkettenfeldern, daneben ein Feld mit den drei möglichen Werten einschließlich Leerwert und ein Feld mit dem Vergleich zweier SQL-Filter
Drei mögliche Werte in einem Feld, das die meisten Abfragen filtern, als hätte es zwei.

Wo er steht

Der Datensatz heißt privacy_info und speichert die Speicherzustände als Zeichenketten, nicht als Wahrheitswerte. Auf die verschachtelten Felder greifst Du mit Punktnotation zu; die Vergleichswerte stehen als Zeichenketten in Anführungszeichen. Weil Werte fehlen können, muss vor der Berechnung einer Quote feststehen, wie ein fehlender Wert behandelt wird.

SQL
SELECT
  event_date,
  privacy_info.analytics_storage AS analytics,
  privacy_info.ads_storage       AS ads,
  COUNT(*)                       AS ereignisse
FROM `projekt.analytics_XXXXXX.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20260801' AND '20260831'
GROUP BY 1, 2, 3
ORDER BY 1, 4 DESC

-- vor jedem Filter laufen lassen. Der Zweck ist zu sehen,
-- wie viele Zeilen in den dritten Topf fallen, denn diese
-- Zahl entscheidet, ob die Falle unten ueberhaupt greift

Erst gruppieren und dann filtern ist hier die ganze Disziplin. Das kostet eine Abfrage, auf einem einzelnen Monat fast nichts, und beantwortet die Frage, von der jede weitere Abfrage abhängt.

Die Falle

Zwei Filter, die sich wie Gegensätze lesen, sind keine. analytics_storage = 'No' wählt Ereignisse mit ausdrücklicher Ablehnung. analytics_storage != 'Yes' wählt Ablehnungen plus alles ohne festgehaltenen Zustand – denn in SQL ergibt ein Vergleich gegen einen Leerwert wieder einen Leerwert und nicht wahr, sodass der erste Filter die leeren Zeilen still fallen lässt und der zweite sie behält.

Eine Einwilligungsquote auf dem ersten fällt höher aus als eine auf dem zweiten, auf denselben Daten, für denselben Tag. Von außen ist keine der beiden offensichtlich falsch; beide liefern einen plausiblen Prozentsatz. Welche stimmt, hängt davon ab, was die Leerwerte tatsächlich sind, und das ist eine Frage an die Umsetzung, nicht an SQL.

Leerwerte bedeuten meistens, dass beim Auslösen des Ereignisses kein Einwilligungssignal beim Tag ankam – ein Treffer vor der Entscheidung am Banner, eine Seite, auf der die Einwilligungsplattform nicht lud, ein serverseitiges Ereignis, das ohne durchgereichten Zustand gebaut wurde. Das sind drei verschiedene Lagen mit drei verschiedenen Bedeutungen, und sie mit ausdrücklichen Ablehnungen in einen Topf zu werfen, überzeichnet die Ablehnung.

Was damit möglich wird

Der Zustand liegt für jedes Ereignis vor, nicht nur für die gesamte Property. Deshalb lässt er sich zusammen mit den anderen Feldern derselben Ereigniszeile auswerten.

Text
nach Seite     welche Seiten am haeufigsten abgelehnt werden
               meist die, auf denen das Banner mit dem Inhalt
               um dieselbe Bildschirmflaeche ringt

nach Geraet    Mobil und Rechner unterscheiden sich weit
               genug, dass eine Gesamtzahl keines von beiden
               beschreibt

nach Quelle    Verkehr aus einem Link innerhalb einer App
               verhaelt sich anders als ein Direktbesuch

nach Stunde    eine Auslieferung, die das Banner zerlegt,
               zeigt sich als Stufe zu einer bestimmten
               Stunde - was ein Tageswert vollstaendig
               glaettet

Der Schnitt nach Seite lohnt sich am schnellsten, weil er aus einer Zahl, mit der niemand etwas anfangen kann, eine Liste konkreter Seiten macht.

Zwei Dinge, die über die Zeile zu wissen sind

Ein einzelner Besuch kann Ereignisse in mehr als einem Zustand enthalten. Wer ein Banner mitten in einer Sitzung annimmt, hinterlässt unter derselben Pseudokennung eine Spur abgelehnter Ereignisse, gefolgt von erteilten – was richtig ist und jede Abfrage leise zerlegt, die einen Zustand je Nutzer annimmt.

Und das Exportschema wächst. Der Ereignistabelle wurden schon Felder hinzugefügt und werden wieder welche hinzugefügt, was das Argument gegen SELECT * in allem Nachgelagerten ist: Eine unangekündigt eintreffende Spalte ist harmlos für eine Abfrage, die ihre Spalten benennt, und ein gebrochener Ladeauftrag für eine, die es nicht tut.

Fragen und Antworten

Warum enthält ein Export manchmal keine einzige Ablehnung?

Meist, weil der Consent Mode im einfachen Modus läuft. Dort lädt das Tag erst nach der Einwilligung; wer ablehnt, sendet nichts, und im Export stehen nur Ereignisse mit erteilter Einwilligung oder ohne festgehaltenen Zustand. Eine Einwilligungsquote lässt sich aus einem solchen Export nicht bilden, sie muss dann aus den Aufzeichnungen der Einwilligungsplattform kommen.

Wie lassen sich die Leerwerte den drei Lagen aus dem Artikel zuordnen?

Über die übrigen Felder derselben Zeile, denn jede der drei Lagen hinterlässt ein eigenes Muster:

  • Treffer vor der Entscheidung am Banner ballen sich am Anfang einer Sitzung. Werden die Ereignisse je Sitzung nach event_timestamp geordnet, stehen die Leerwerte vorn, gefolgt von Ereignissen mit festgehaltenem Zustand.
  • Eine Seite, auf der die Einwilligungsplattform nicht lud, zeigt sich im Schnitt nach Seite oder nach Stunde: Leerwerte gehäuft auf bestimmten Seiten oder Seitenvorlagen oder als Stufe ab dem Zeitpunkt einer Auslieferung.
  • Serverseitige Ereignisse ohne durchgereichten Zustand lassen sich am Ereignisnamen erkennen, wenn nur der Server sie sendet; sie sind dann durchgehend leer, unabhängig von Seite und Uhrzeit.

In der Abfrage hilft es, fehlende Werte als dritte Gruppe sichtbar zu halten, statt ihre Behandlung dem Vergleich zu überlassen. COALESCE(privacy_info.analytics_storage, 'leer') ersetzt den Leerwert durch eine eigene Ausprägung, die ausdrücklich in jeder Gruppierung und jedem Filter erscheint. Was „leer“ bedeutet, wird damit einmal an einer Stelle entschieden und nicht stillschweigend in jedem WHERE.

Warum grenzt die Abfrage über _TABLE_SUFFIX ab und nicht allein über event_date?

Aus zwei Gründen. Erstens rechnet BigQuery Abfragen im On-Demand-Tarif nach gelesenen Daten ab, und nur ein Filter auf _TABLE_SUFFIX bestimmt, welche Tagestabellen überhaupt gelesen werden; ein Filter allein auf event_date liest zunächst jede Tabelle, die der Platzhalter erfasst, und verwirft die Zeilen erst danach.

Zweitens erfasst events_* auch die Tabellen des Streaming-Exports, events_intraday_…, deren Suffix mit intraday_ beginnt. Ein Bereich aus Datumswerten wie ‘20260801’ bis ‘20260831’ schließt sie aus. Reicht ein Zeitraum bis an den laufenden Tag heran, liest ein Filter nur auf event_date dagegen deren vorläufige Zeilen mit.

Lukas Wojcik

Lukas Wojcik

Systems architect and technology enthusiast specializing in scalable tracking solutions, GMP Stack (GA4 & GTM), and robust backend architectures. Advocate for clean code and privacy-first design.

Kontakt

Eine Frage oder eine Idee für ein Webprojekt? Schreib mir kurz, worum es geht. Das Formular nutzt reCAPTCHA zum Schutz vor Spam.

Kommentar schreiben

Abweichende Zahlen aus anderen Konten und Rückfragen zur Einrichtung sind hier willkommen.

Die E-Mail-Adresse wird nicht veröffentlicht. Pflichtfelder sind mit einem Stern versehen.