GROUP BY y ORDER BY sin dolor de cabeza
5 min de lecturaGROUP BY: agrupar para resumir
GROUP BY divide las filas en grupos basándose en los valores de una o más columnas. Después de agrupar, las funciones de agregación (COUNT, SUM, AVG) calculan un resultado por cada grupo en lugar de un total general.
Sin GROUP BY:
-- Un solo total para todo el sitio
SELECT COUNT(DISTINCT user_pseudo_id) AS usuarios_totales
FROM tabla_eventos
WHERE traffic_source.medium = 'organic'
-- Resultado: 1 fila con el total
Con GROUP BY:
-- Un total por cada dispositivo
SELECT
device.category AS dispositivo,
COUNT(DISTINCT user_pseudo_id) AS usuarios
FROM tabla_eventos
WHERE traffic_source.medium = 'organic'
GROUP BY dispositivo
-- Resultado: 3 filas (desktop, mobile, tablet)
La regla fundamental: toda columna que aparece en el SELECT y que no está dentro de una función de agregación debe estar en el GROUP BY. Si se incluye dispositivo en el SELECT, debe estar en el GROUP BY. Si se olvida, BigQuery devuelve un error.
Agrupar por múltiples columnas
Se pueden agrupar datos por dos o más dimensiones simultáneamente. Cada combinación única genera una fila en los resultados:
-- Sesiones por dispositivo Y país
SELECT
device.category AS dispositivo,
geo.country AS pais,
COUNT(DISTINCT session_id) AS sesiones
FROM tabla_eventos
WHERE traffic_source.medium = 'organic'
GROUP BY dispositivo, pais
ORDER BY sesiones DESC
Si hay 3 tipos de dispositivo y 20 países, el resultado puede tener hasta 60 filas (3 x 20), una por cada combinación. En la práctica suele ser menos, porque no todas las combinaciones existen en los datos.
HAVING: filtrar después de agrupar
WHERE filtra filas antes de la agregación. HAVING filtra después. Esto es importante porque permite excluir grupos con datos insuficientes:
-- Solo keywords con al menos 100 impresiones
SELECT
query AS keyword,
SUM(clicks) AS clics,
SUM(impressions) AS impresiones
FROM `your-project.searchconsole.searchdata_site_impression`
WHERE data_date BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 28 DAY) AND CURRENT_DATE()
GROUP BY keyword
HAVING impresiones >= 100
ORDER BY clics DESC
No se puede usar WHERE impresiones >= 100 porque impresiones es un resultado de SUM, que no existe hasta después de la agregación. HAVING actúa sobre los resultados ya agregados.
Un uso muy común en SEO: excluir páginas o keywords con poco volumen para que los promedios y porcentajes sean estadísticamente significativos, como en la query de engagement rate por landing page.
ORDER BY: ordenar los resultados
ORDER BY define el orden en que aparecen las filas en los resultados. Sin esta cláusula, el orden es arbitrario e impredecible.
-- Ordenar de mayor a menor (descendente)
ORDER BY sesiones DESC
-- Ordenar de menor a mayor (ascendente, valor por defecto)
ORDER BY posicion_media ASC
-- Ordenar por múltiples columnas
ORDER BY pais ASC, sesiones DESC
La dirección por defecto es ASC (ascendente). DESC (descendente) se usa explícitamente cuando se quieren ver primero los valores más altos (más clics, más sesiones, más impresiones). Para métricas donde lo bajo es lo que interesa (posición media baja, páginas con menos tráfico), se usa ASC.
Cuando se ordena por múltiples columnas, la primera columna es el criterio principal. La segunda se aplica solo cuando hay empates en la primera.
LIMIT: restringir la cantidad de resultados
LIMIT se usa junto con ORDER BY para obtener los N primeros o últimos resultados:
-- Top 50 keywords por clics
SELECT query, SUM(clicks) AS clics
FROM gsc_data
GROUP BY query
ORDER BY clics DESC
LIMIT 50
Casi todas las queries del catálogo de Queryteca terminan con ORDER BY ... LIMIT N. Es una combinación fundamental que convierte una lista desordenada de miles de filas en un ranking accionable de las N más relevantes.
El orden de ejecución importa
SQL no se ejecuta en el orden en que se escribe. El orden real de procesamiento es:
FROM: identifica la tabla.WHERE: filtra filas antes de agrupar.GROUP BY: agrupa las filas que pasaron el filtro.HAVING: filtra grupos después de agregar.SELECT: elige las columnas y calcula las funciones.ORDER BY: ordena los resultados finales.LIMIT: recorta los N primeros.
Conocer este orden ayuda a entender por qué ciertas cosas no funcionan. Por ejemplo, no se puede usar un alias definido en el SELECT dentro del WHERE (porque WHERE se ejecuta antes), pero sí dentro del ORDER BY (que se ejecuta después). BigQuery es flexible con esto y permite usar aliases en HAVING y ORDER BY.
Ejemplo completo
Una consulta que combina todas las cláusulas vistas hasta ahora, similar a la query de top keywords por clics:
SELECT
query AS keyword,
SUM(clicks) AS clics,
SUM(impressions) AS impresiones,
ROUND(SAFE_DIVIDE(SUM(clicks), SUM(impressions)) * 100, 2) AS ctr,
ROUND(SUM(sum_top_position + impressions) / SUM(impressions), 2) AS posicion
FROM `your-project.searchconsole.searchdata_site_impression`
WHERE data_date BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 28 DAY) AND CURRENT_DATE()
AND search_type = 'WEB'
GROUP BY keyword
HAVING impresiones >= 50
ORDER BY clics DESC
LIMIT 100
Siguiente paso
Con las cláusulas de agregación y ordenamiento dominadas, el siguiente desafío frecuente en BigQuery es el manejo de fechas: _TABLE_SUFFIX, FORMAT_DATE, DATE_SUB y compañía. Las fechas son la parte que más errores genera al principio, pero con las funciones correctas se manejan con facilidad.
Queries para practicar
Sesiones orgánicas por día en los últimos 30 días
Permite visualizar el volumen diario de sesiones de tráfico orgánico para detectar tendencias, picos y caídas en los últimos 30 días.
Limpiar y normalizar una lista de URLs con SQL
Normaliza una lista de URLs eliminando parámetros de tracking, fragmentos, barras finales duplicadas y forzando minúsculas. Es el paso previo imprescindible antes de cualquier análisis de URLs.
Performance por dispositivo (mobile, desktop, tablet)
Compara el rendimiento de búsqueda por tipo de dispositivo. Permite detectar diferencias de posicionamiento o CTR entre mobile y desktop que indiquen problemas de UX o indexación.
¿Listo para practicar? Explora el catálogo de queries
Ver catálogo