QUERY(rango, "SELECT a, b") en Google Sheets extrae dos columnas. Si ahí te quedaste, te saltaste la parte que hace que QUERY valga la pena.
La cláusula SELECT soporta funciones de agregación - SUM, COUNT, AVG, MAX, MIN - combinadas con GROUP BY y PIVOT. Esa combinación convierte un volcado de transacciones en bruto en una tabla resumen sin SUMAR.SI, sin columnas auxiliares, sin extender rangos manualmente cada vez que alguien agrega una fila. Es la parte que la mayoría de los tutoriales evita porque es más difícil de explicar, y es precisamente la que cambia cómo construyes modelos.
Las letras de columna son relativas al rango, no a la hoja
Orientación rápida antes de entrar en materia: a en una fórmula QUERY no significa la columna A de la hoja. Significa la primera columna del rango que le pasaste a QUERY. Si tu rango es transacciones!C:H, entonces a corresponde a la columna C, b a la columna D, y así sucesivamente.
Esto importa especialmente cuando tus datos no empiezan en la columna A - que es casi siempre. Agrega una columna a la izquierda de tu rango fuente y todas las fórmulas QUERY que dependen de él se rompen en silencio. Algo que hay que contemplar desde el diseño.
Un comportamiento relacionado que confunde seguido: puedes filtrar por una columna sin incluirla en la salida. SELECT a, b WHERE c = 'Marketing' es válido - la columna c aplica el filtro pero no aparece en el resultado. La mayoría asume que las columnas en WHERE deben estar en SELECT. No es así.
SELECT con agregación: reemplaza tus SUMAR.SI
Imagina que tu pestaña de transacciones tiene 5 columnas: fecha, área, categoría, proveedor, monto. Quieres un resumen por área y categoría - el tipo de tabla que normalmente construirías con 20 SUMAR.SI distribuidos en una matriz, cada uno hardcodeado a un par área-categoría específico.
=QUERY(transacciones!A:E,
"SELECT B, C, SUM(E)
WHERE A >= date '2026-01-01'
GROUP BY B, C
ORDER BY B, SUM(E) DESC
LABEL SUM(E) 'Total'")
Esa única fórmula construye el resumen completo. Las nuevas áreas aparecen automáticamente. Las nuevas categorías aparecen automáticamente. El filtro de fecha excluye datos de períodos anteriores. La cláusula LABEL al final renombra la columna agregada desde el nombre por defecto de QUERY ("sum monto") a algo legible.
En un registro de 10,000 transacciones representativo, este enfoque corre en aproximadamente 600ms - frente a unos 1,100ms de una matriz equivalente de SUMAR.SI escaneando el mismo rango. La diferencia crece con el volumen de filas.
Para extraer un P&L desde una pestaña de detalle: agrega WHERE D = 'COGS' para aislar líneas de costo, o elimina la columna de categoría del GROUP BY para consolidar solo por área. Una fórmula, múltiples vistas.
PIVOT: tablas cruzadas que se actualizan solas
La palabra clave PIVOT de QUERY crea una tabla cruzada que se actualiza cuando los datos fuente cambian, sin necesidad de agregar columnas manualmente.
=QUERY(transacciones!A:E,
"SELECT B, SUM(E)
WHERE A >= date '2026-01-01'
GROUP BY B
PIVOT C")
Las filas son áreas. Las columnas son los valores distintos que aparezcan en la columna C - de forma dinámica. No especificas cuáles son esas columnas. QUERY lee los datos y crea una columna por cada valor único. En un conjunto de gastos que incorpora nuevas categorías con el tiempo, este es el único enfoque que no requiere agregar columnas manualmente cada trimestre.
Una limitación real: los encabezados de columna generados por PIVOT provienen de los valores del dato, por lo que son difíciles de referenciar en fórmulas posteriores. Si necesitas que la salida alimente cálculos adicionales, GROUP BY sin PIVOT es el camino más seguro - maneja la presentación en tabla cruzada por separado.
Qué pasa cuando tus datos están sucios
Esto es lo que la mayoría de los tutoriales de QUERY no te dicen: SELECT funciona bien con datos limpios. Los datos reales lo rompen de maneras específicas y difíciles de diagnosticar.
La inferencia de tipos es silenciosa. QUERY escanea la primera parte de una columna y decide su tipo. Según la documentación oficial de Google Visualization API Query Language1, cuando más del 50% de los valores no vacíos de una columna son de un tipo determinado, ese tipo se asigna a toda la columna. Si tu columna de montos tiene 4,997 números y 3 celdas donde alguien escribió "N/A" o "pendiente", QUERY puede clasificar la columna entera como texto. Cuando eso ocurre, SUM(E) devuelve un resultado en blanco - sin error, sin advertencia. Solo lo notas cuando el resumen no cuadra.
La solución: una columna auxiliar con =IFERROR(VALUE(E2), 0), que convierte "N/A" en 0 y deja los números intactos. Ejecuta QUERY contra la auxiliar, no contra la columna original.
Las filas en blanco generan el mismo problema. Los exports de ERP y bases de datos frecuentemente incluyen filas en blanco intercaladas (común en SAP, Oracle y la mayoría de exports CSV de sistemas legacy). Estas interrumpen el escaneo de detección de tipos de QUERY. Agregar WHERE E IS NOT NULL antes de otras condiciones las filtra. Definir el rango como columnas completas (A:E) en lugar de un rango fijo como A1:E5000 también ayuda - QUERY maneja mejor los datos dispersos en rangos abiertos.
Los formatos de fecha mixtos son el peor caso. La cláusula WHERE de QUERY para fechas requiere el formato date 'YYYY-MM-DD'. Si la columna de fecha tiene algunos valores guardados como texto y otros como valores de fecha reales, el filtro devuelve un resultado vacío sin avisar. Prueba con =TIPO(A2): 1 significa número (los valores de fecha se almacenan como números en Sheets, que es lo que buscas), 2 significa texto (problema). Las fechas en texto necesitan =FECHANUMERO(A2) en una columna auxiliar antes de que QUERY pueda filtrarlas.
El patrón de fallo en todos los casos es el silencio - resultados vacíos, no mensajes de error. Antes de confiar en una fórmula QUERY sobre un dataset real, ejecuta SELECT COUNT(a) primero. Si el conteo no coincide con el número de filas esperado, algo está mal con la inferencia de tipos antes de tocar la consulta real.
SELECT dinámico: reportes controlados por desplegable
El patrón de QUERY más útil para reportes es uno que casi nunca se documenta: construir la cadena SELECT desde una referencia a celda.
=QUERY(datos!A:F,
"SELECT " & B1 & " WHERE A >= date '2026-01-01'")
Si B1 contiene "a, b, d", la consulta devuelve las columnas 1, 2 y 4. Cambia B1 a "a, c, f" y la salida cambia al instante.
Combina esto con una validación de datos en desplegable - una celda donde el usuario elige "Vista Financiera" o "Vista Operativa" - y un VLOOKUP que mapea cada opción a una cadena de columnas, y tendrás un único rango de salida que se reconfigura según la selección del usuario. Sin Apps Script, sin menús personalizados.
La versión práctica: si distintos stakeholders necesitan subconjuntos de columnas diferentes (finanzas quiere márgenes, operaciones quiere headcount, dirección quiere KPIs), este patrón los sirve a todos desde una sola pestaña de datos sin mantener 3 rangos de salida separados. La inserción de columnas sigue rompiéndolo - la limitación fundamental del direccionamiento por letra - pero para esquemas estables es la solución más limpia disponible dentro de una fórmula.
QUERY vs FILTER vs VLOOKUP
| Tarea | Herramienta ideal | Tiempo aprox. en 10,000 filas |
|---|---|---|
| Devolver filas que cumplen una condición | QUERY o FILTER | ~400ms (similar) |
| Resumir por grupo (reemplaza matriz SUMAR.SI) | QUERY GROUP BY | ~600ms vs ~1,100ms para SUMAR.SI equivalente |
| Salida en tabla cruzada | QUERY PIVOT | ~900ms |
| Búsqueda de un valor por clave | VLOOKUP / INDEX-MATCH | ~80ms |
| Selección dinámica de columnas | QUERY con concatenación | Igual que la consulta base |
| Filtrado por regex / coincidencia parcial | QUERY (palabra clave MATCHES) | ~700ms |
VLOOKUP gana en búsquedas de un solo valor por amplio margen - es más rápido y la fórmula es más fácil de auditar. FILTER es mejor que QUERY para selección condicional de filas cuando no necesitas agregación, porque la sintaxis es más transparente y maneja condiciones de arreglo de forma nativa. QUERY toma el control cuando necesitas GROUP BY, PIVOT o selección de columnas agregadas. Usar QUERY para una búsqueda simple de un valor es como tomar la autopista para ir a la tienda de la esquina.
Un pull de ingresos real: export de pagos → ARR mensual
El patrón anterior se vuelve concreto aquí. Tienes una pestaña llamada export_pagos con columnas: fecha, id_cliente, mrr_usd, plan, estado. Quieres un resumen mensual por plan en una pestaña de reporte separada.
=QUERY(export_pagos!A:E,
"SELECT
YEAR(A), MONTH(A), D,
SUM(C)
WHERE E = 'active'
GROUP BY YEAR(A), MONTH(A), D
ORDER BY YEAR(A), MONTH(A), D
LABEL YEAR(A) 'Año', MONTH(A) 'Mes', D 'Plan', SUM(C) 'MRR'")
Esto produce una tabla de 4 columnas: año, mes, plan, MRR. Cada vez que la pestaña de export se actualiza, esta fórmula recalcula. Sin fórmulas adicionales, sin matriz de SUMAR.SI que extender cuando agregas un nuevo tier de plan.
El punto crítico: si mrr_usd tiene aunque sea un valor no numérico - un script de exportación que escribió "null" o "-" en lugar de 0 - SUM devuelve en blanco para toda la columna. Envuelve la fuente: =IFERROR(VALUE(C2), 0) en una columna auxiliar y apunta QUERY a la auxiliar.
A abril de 2026, las funciones YEAR() y MONTH() de QUERY funcionan sobre celdas con formato de fecha, pero no sobre cadenas de texto que parecen fechas. Si tu export escribe las fechas como texto ("2026-01-15" en lugar de un valor de fecha de Sheets), agrega una columna auxiliar con =FECHANUMERO(A2) y referencia esa en su lugar.
Cuando el mapeo de letras de columna en un rango ancho se vuelve confuso - que ocurre rápido en un export de 15 columnas - Elige un plan de ModelMonkey.
Preguntas frecuentes
¿Qué diferencia hay entre SELECT a, b y SELECT * en QUERY de Google Sheets?
SELECT * devuelve todas las columnas del rango en el orden original. SELECT a, b devuelve solo las columnas especificadas y permite reordenarlas, aplicar funciones de agregación (SUM, AVG, COUNT) y renombrarlas con LABEL. Para cualquier reporte que no necesite todas las columnas fuente, SELECT a, b es la opción correcta.
¿Por qué mi SELECT con SUM devuelve una celda en blanco en lugar de un número?
Casi siempre es un problema de inferencia de tipos. QUERY detectó al menos un valor no numérico en la columna que intentas sumar - un "N/A", un texto vacío o una celda con fórmula que devuelve texto - y clasificó toda la columna como tipo texto. Solución: crea una columna auxiliar con =IFERROR(VALUE(celda), 0) y apunta QUERY a esa columna en lugar de a la original.
¿Puedo usar WHERE con una columna que no aparece en SELECT?
Sí. En Google Sheets QUERY puedes filtrar por cualquier columna del rango aunque no esté listada en SELECT. SELECT a, b WHERE c = 'Activo' es válido: filtra por la columna c pero solo muestra las columnas a y b en la salida.
¿PIVOT en QUERY es lo mismo que una tabla dinámica de Sheets? No exactamente. QUERY PIVOT crea una tabla cruzada dinámica dentro de una fórmula, sin interfaz gráfica. Ventaja: se puede anidar con otras fórmulas y se actualiza al instante cuando cambian los datos fuente. Desventaja: los encabezados de columna generados son difíciles de referenciar en cálculos posteriores. Para análisis exploratorio, la tabla dinámica nativa es más flexible; para reportes automatizados que alimentan dashboards, QUERY PIVOT es más confiable.
¿Cuándo conviene usar FILTER en lugar de QUERY SELECT? Usa FILTER cuando solo necesitas seleccionar filas según una o más condiciones, sin agregación ni reordenamiento de columnas. La sintaxis de FILTER es más corta y maneja condiciones de arreglo de forma nativa. Usa QUERY cuando necesitas GROUP BY, PIVOT, ORDER BY o funciones de agregación - ahí FILTER no tiene equivalente directo.
Footnotes
-
Google Developers. Query Language Reference (Version 0.7) - sección "Column types and data type inference". Documentación oficial de Google Visualization API, actualizada a 2025. developers.google.com/chart/interactive/docs/querylanguage ↩