Praxsuite

Agregaciones

Vincent Depassier · 29 de agosto de 2026

PraxQL — Agregaciones

PraxQL soporta GROUP BY, HAVING y funciones de agregación en el mismo cuerpo de consulta. No hay endpoint aparte ni función serverless que escribir.


Cuándo usarlas

Agregá cuando querés un número sobre un conjunto de filas y no las filas: ingresos por cliente, pedidos por estado, ticket promedio por región, el pedido más grande, la fecha más antigua.

Si querés las filas y el número, son dos consultas. Está al final.


Las cinco funciones

Función

Descripción

count

Cantidad de filas

sum

Suma de valores numéricos

avg

Promedio

min

Valor mínimo

max

Valor máximo

Los nombres no distinguen mayúsculas. Cualquier cosa fuera de esta lista devuelve 400 INVALID_QUERY.


El select de una agregación

En una consulta de agregación cada elemento de select es un objeto, no un string suelto:

Campo

Obligatorio

Descripción

field

sí

Columna a agregar o por la que agrupar

fn

no

Función de agregación. Omitila para una columna de dimensión.

alias

no

Nombre de la columna resultante en la respuesta

"select": [
  { "field": "Region", "alias": "Region" },
  { "field": "Total",  "fn": "sum",   "alias": "IngresoTotal" },
  { "field": "Id",     "fn": "count", "alias": "CantidadPedidos" },
  { "field": "Total",  "fn": "avg",   "alias": "TicketPromedio" },
  { "field": "Total",  "fn": "max",   "alias": "PedidoMayor" }
]

La misma columna puede aparecer varias veces con funciones distintas, como hace Total acá.


groupBy

Las columnas por las que agrupar. Todo elemento de select sin fn tiene que aparecer acá — es una regla de SQL, no de PraxQL, y romperla es la causa más común de una agregación rechazada.

"groupBy": ["Region"]

having

Filtros aplicados después de agrupar. Misma sintaxis que where, más el campo fn para filtrar sobre un agregado:

"having": [
  { "field": "Total", "fn": "sum", "op": "gt", "value": 10000 }
]

Ejemplo completo

Ingresos por región para pedidos posteriores a enero de 2026, dejando solo las regiones por encima de 10.000:

{
  "refs": {
    "Pedidos": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
  },
  "query": {
    "from": "Pedidos",
    "select": [
      { "field": "Region", "alias": "Region" },
      { "field": "Total",  "fn": "sum",   "alias": "IngresoTotal" },
      { "field": "Id",     "fn": "count", "alias": "CantidadPedidos" },
      { "field": "Total",  "fn": "avg",   "alias": "TicketPromedio" }
    ],
    "where": [
      { "field": "Fecha", "op": "gte", "value": "2026-01-01" }
    ],
    "groupBy": ["Region"],
    "having": [
      { "field": "Total", "fn": "sum", "op": "gt", "value": 10000 }
    ],
    "orderBy": [
      { "field": "IngresoTotal", "dir": "desc" }
    ],
    "limit": 20
  }
}

El orden en que pasan las cosas

  1. FROM — elegir la tabla

  2. WHERE — filtrar filas individuales

  3. GROUP BY — agrupar lo que sobrevivió

  4. Funciones de agregación — calcular por grupo

  5. HAVING — filtrar los grupos

  6. ORDER BY — ordenarlos

  7. LIMIT / OFFSET — paginar

La consecuencia práctica: where filtra filas antes de agrupar, having filtra grupos después de agregar. "Regiones cuyos pedidos completados suman más de 10.000" es un where sobre el estado más un having sobre la suma — poner el estado en having hace otra pregunta, y en general no devuelve nada.


Permisos

Agregar necesita dos flags, y los dos vienen en `false` por defecto:

Flag

Dónde

Sin él

AllowAggregations

scope de tabla

cualquier groupBy o agregado sobre esa tabla devuelve 403 SCOPE_VIOLATION

CanAggregate

scope de columna

esa columna en particular no se puede agregar

Así que una key recién creada no puede agregar nada hasta que lo habilites. Es a propósito: un agregado puede revelar la forma de datos que quien llama no tiene permitido leer fila por fila.


Agregaciones y relaciones

Los agregados no corren dentro de una subconsulta de relación. Podés agregar la tabla principal, o traer las filas relacionadas y agregarlas del lado del cliente.

Cuando necesitás totales y filas de detalle, mandá dos consultas. Son los mismos viajes que una consulta combinada habría costado internamente, y cada mitad queda cacheable por su cuenta.