En el ecosistema actual del marketing digital, los datos han dejado de ser un simple accesorio para convertirse en el motor principal de la estrategia. Sin embargo, muchos profesionales se encuentran con una barrera invisible: los informes estándar de la interfaz de Google Analytics 4 (GA4).
Aunque útiles para una visión rápida, estos informes suelen estar limitados por el muestreo, los umbrales de datos y una estructura rígida que no siempre responde a las preguntas más complejas del negocio.
Es aquí donde la exportación nativa a BigQuery transforma las reglas del juego.
Estructura de las tablas de GA4 en BigQuery
Una vez configurada la exportación, GA4 crea automáticamente tablas en BigQuery con una estructura específica que es tenéis que entender, porque no es una tabla particionada clásica. En este artículo, no entraré en el caso especial de los datos en streaming, asumiré que usáis la exportación diaria.
Nomenclatura de las tablas
Las tablas de GA4 en BigQuery siguen este patrón:
`<id_proyecto_gcp>.analytics_<id_analytics>.events_YYYYMMDD`

Donde:
- <id_proyecto_gcp>: Es el ID de vuestro proyecto GCP
- <id_analytics>: Es un dataset que se crea automáticamente con el ID numérico de vuestra propiedad GA4
- events_YYYYMMDD: Cada día se crea una nueva tabla con el formato de fecha (año, mes, día)
Estructura de datos: modelo basado en eventos
Columnas principales de la tabla events_
Las columnas más importantes que encontraréis en las tablas de eventos son:
| Columna | Tipo | Descripción |
| event_date | STRING | Fecha del evento en formato YYYYMMDD |
| event_timestamp | INTEGER | Timestamp en microsegundos desde el 1 de enero de 1970 |
| event_name | STRING | Nombre del evento (page_view, purchase, etc.) |
| event_params | RECORD | Array de parámetros del evento |
| event_previous_timestamp | INTEGER | Timestamp del evento anterior del mismo usuario |
| event_value_in_usd | FLOAT | Valor del evento en USD (si aplica) |
| event_bundle_sequence_id | INTEGER | ID de secuencia del bundle de eventos |
| event_server_timestamp_offset | INTEGER | Offset del timestamp del servidor |
| user_id | STRING | ID del usuario (si está configurado) |
| user_pseudo_id | STRING | ID pseudo-anónimo del usuario (client_id) |
| privacy_info | RECORD | Información sobre el consentimiento y privacidad |
| user_properties | RECORD | Propiedades del usuario |
| user_first_touch_timestamp | INTEGER | Timestamp del primer contacto del usuario |
| user_ltv | RECORD | Valor de vida del usuario |
| device | RECORD | Información del dispositivo |
| geo | RECORD | Información geográfica |
| app_info | RECORD | Información de la aplicación |
| collected_traffic_source | RECORD | Fuente de tráfico recopilada en el evento actual |
| session_traffic_source_last_clicks | RECORD | Última fuente de tráfico con clic de la sesión |
| traffic_source | RECORD | Información de la fuente de tráfico |
| stream_id | STRING | ID del flujo de datos |
| platform | STRING | Plataforma (WEB, IOS, ANDROID) |
| event_dimensions | RECORD | Dimensiones del evento |
| ecommerce | RECORD | Información de e-commerce |
| items | RECORD | Array de items (para eventos de e-commerce) |
Para más información sobre estos campos y subcampos, mirad la documentación.
Estructura de datos anidados (nested data)
Una característica importante de las tablas de GA4 es el uso extensivo de datos anidados (nested data en inglés). Esto significa que algunas columnas contienen estructuras complejas dentro de ellas. Por ejemplo, event_params es un array de registros que contiene:
- key: El nombre del parámetro
- value: El valor del parámetro (que puede ser string, int, float, o double)

Este formato poco habitual existe porque los parámetros de los eventos en Google Analytics dependen del evento. Para una página vista, el código enviará un payload similar al que indico a continuación:
{
"event_date": "20250115",
"event_timestamp": 1705324800000000,
"event_name": "page_view",
"event_params": [
{
"key": "page_location",
"value": "https://www.ejemplo.com/productos/categoria-1"
},
{
"key": "page_title",
"value": "Categoría de Productos - Mi Tienda"
},
{
"key": "page_referrer",
"value": "https://www.google.com/search?q=productos"
},
{
"key": "ga_session_id",
"value": 1705324800
},
{
"key": "entrances",
"value": 1
},
{
"key": "engagement_time_msec",
"value": 5000
}
],
"user_pseudo_id": "1234567890.1234567890",
"device": {
"category": "desktop",
"operating_system": "Windows",
"browser": "Chrome"
},
"geo": {
"country": "Spain",
"city": "Madrid"
}
}
Pero para un evento de registro, tendremos lo siguiente:
{
"event_date": "20250115",
"event_timestamp": 1705324800000000,
"event_name": "sign_up",
"event_params": [
{
"key": "page_location",
"value": "https://www.ejemplo.com/registro"
},
{
"key": "method",
"value": "Google"
}
],
"user_pseudo_id": "1234567890.1234567890",
"device": {
"category": "desktop",
"operating_system": "Windows",
"browser": "Chrome"
},
"geo": {
"country": "Spain",
"city": "Madrid"
}
}
Como véis, lo que tenemos dentro de event_params depende del evento, y es la gran diferencia entre GA4 y la versión anterior: FLEXIBILIDAD. Para conservar esta ventaja en BigQuery, Google ha tenido que usar una estructura en sus tablas que permite recibir información diferente en una única columna. En efecto, no podemos tener un sistema que crea una columna nueva cada vez que detecta un parámetro diferente, sería inviable y poco eficiente.
Por eso, tenemos este formato poco habitual y que nos da algunos dolores de cabeza en SQL. Para trabajar con estos datos anidados, necesitaréis usar funciones específicas de BigQuery como UNNEST.
Consultas básicas
Si no necesitamos usar los datos de las columnas que requieren el uso de UNNEST, podemos usar SQL clásico cómo siempre lo hemos hecho.
Consultar eventos de un día específico
La forma más básica de consultar datos de GA4 es seleccionar eventos de una fecha concreta:
SELECT
event_name,
COUNT(*) as event_count
FROM `xxxxxxx.analytics_397818756.events_20250115`
GROUP BY event_name
ORDER BY event_count DESC

Consultar múltiples días usando wildcards
Para consultar datos de varios días, podéis usar el carácter comodín estrella (*) en el nombre de la tabla:
SELECT
event_name,
COUNT(*) as event_count
FROM `xxxxxxx.analytics_397818756.events_202501*`
GROUP BY event_name
ORDER BY event_count DESC
Esta consulta procesa todas las tables de enero del 2025 (todas las tablas que empiezan por event_202501).
Cuando usáis wildcards que incluyen muchos días y dónde no podemos usar un wildcard, siempre debéis filtrar por `_TABLE_SUFFIX` para limitar las tablas que se escanean y reducir costes. Es parecido al sistema de optimización que se usa para las particiones.
SELECT
event_date,
event_name,
COUNT(*) as event_count
FROM `xxxxxxx.analytics_397818756.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20250101' AND '20250231'
GROUP BY event_date, event_name
Consultar eventos más recientes
Para obtener los datos más recientes disponibles, podéis usar una subconsulta para encontrar la fecha máxima:
DECLARE max_date STRING;
SET max_date = (
SELECT MAX(_TABLE_SUFFIX)
FROM `xxxxxxx.analytics_397818756.events_*`
);
SELECT
event_name,
COUNT(*) as event_count
FROM `xxxxxxx.analytics_397818756.events_*`
WHERE _TABLE_SUFFIX = max_date
GROUP BY event_name
ORDER BY event_count DESC
Con este código, vamos a ejecutar dos consultas diferentes:
1. Una para obtener la fecha máxima
2. Otra para ejecutar el resto del código SQL

Si usamos una subconsulta clásica, el coste sería más elevado porque BigQuery seleccionaría todos los datos y después filtraría en base al resultado de la subconsulta.
SELECT
event_name,
COUNT(*) as event_count
FROM `xxxxxxx.analytics_397818756.events_*`
WHERE _TABLE_SUFFIX = (SELECT MAX(_TABLE_SUFFIX) FROM `xxxxxxx.analytics_397818756.events_*`)
GROUP BY event_name
ORDER BY event_count DESC
Trabajar con parámetros de eventos
Los parámetros de eventos son una de las partes más importantes y complejas de trabajar con datos de GA4 en BigQuery. Cada evento puede tener múltiples parámetros personalizados.
Extraer un parámetro específico
Para extraer un parámetro específico, podéis usar una subconsulta. Lo único que tenéis que mirar antes es si el dato se encuentra en la columnas value.string_value, value.int_value, value.float_value o value.double_value.
El código que vais a generar para extraer los valores de cada uno de vuestros parámetros se parece a un subconsulta:
(SELECT value.string_value
FROM UNNEST(event_params)
WHERE key = 'page_location') as page_location,
En una consulta completa, tendríais, por ejemplo:
SELECT
event_name,
(SELECT value.string_value
FROM UNNEST(event_params)
WHERE key = 'page_location') as page_location,
(SELECT value.string_value
FROM UNNEST(event_params)
WHERE key = 'page_title') as page_title
FROM `xxxxxxx.analytics_397818756.events_*`
WHERE _TABLE_SUFFIX = '20250115'
AND event_name = 'page_view'
LIMIT 100

Si usamos el nombre de un parámetro que no existe, el valor devuelto siempre será NULL y no tendremos ningún error, así que comprobad bien que estáis usando los nombres correctos.
Simplificar la extracción de parámetros
Para hacer vuestras consultas más legibles, podéis crear una CTE para hacer la extracción inicial de los datos que necesitáis, para después seguir con la consulta que os permite contestar a vuestra pregunta. Por ejemplo:
WITH events_with_params AS (
SELECT
event_timestamp,
event_name,
user_pseudo_id,
(SELECT value.string_value
FROM UNNEST(event_params)
WHERE key = 'page_location') as page_location,
(SELECT value.string_value
FROM UNNEST(event_params)
WHERE key = 'page_title') as page_title
FROM `xxxxxxx.analytics_397818756.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20250101' AND '20250131'
AND event_name = 'page_view'
)
SELECT
page_location,
COUNT(*) as page_views
FROM events_with_params
GROUP BY page_location
ORDER BY page_views DESC
LIMIT 10
Enfoque para consultar datos de GA4
Como conclusión, os invito a seguir este proceso para crear vuestras consultas rápidamente.
ETAPA 1: Listar todos las columnas y parámetros que vamos a necesitar. Podéis usar cualquier sistema de IA enchufando la URL de la documentación para ir más rápido. Como consejo, os agrego agregar este artículo en el contexto de la IA, así tendrá más información sobre los datos de GA4 y os devolverá mejores resultados 🙂

ETAPA 2: Hacer lo mismo con los parámetros.

ETAPA 3: Seguir con el análisis. Véis, no es tan complicado al final.