Porcentajes y participación sobre el total
IntermedioLa participación de un grupo es su valor dividido por el total de todos los grupos. sum(count(*)) OVER () trae ese total a cada fila sin una segunda consulta. Tres hábitos evitan errores: multiplicar por 100.0 para no caer en división entera, envolver el denominador en NULLIF(..., 0) y redondear solo al final.
Sintaxis
round(100.0 * count(*) / sum(count(*)) OVER (), 1) -- participación
round(100.0 * count(*) FILTER (WHERE cond) / NULLIF(count(*), 0), 1) -- tasaEjemplo
SELECT
o.payment_method AS medio_de_pago,
count(*) AS pedidos,
round(100.0 * count(*) / sum(count(*)) OVER (), 1) AS pct_del_total,
round(
100.0 * count(*) FILTER (WHERE o.status = 'cancelled') / NULLIF(count(*), 0),
1
) AS pct_cancelados
FROM orders AS o
INNER JOIN restaurants AS r ON r.id = o.restaurant_id
WHERE r.city_id = 7
AND o.placed_at >= '2025-08-01'
AND o.placed_at < '2025-09-01'
GROUP BY o.payment_method
ORDER BY pedidos DESC, medio_de_pago;Resultado
| medio_de_pago | pedidos | pct_del_total | pct_cancelados |
|---|---|---|---|
| card | 82 | 56.6 | 4.9 |
| wallet | 38 | 26.2 | 5.3 |
| cash | 25 | 17.2 | 16.0 |
Cómo leerlo
Son los pedidos de agosto de 2025 en Lima (city_id = 7). pct_del_total suma 100 entre los tres medios de pago: es la participación de cada uno. pct_cancelados es otra cosa, una tasa dentro de cada grupo: de los 25 pedidos en efectivo se canceló el 16 %, más del triple que con tarjeta. Conviene aclarar siempre cuál de los dos porcentajes estás mostrando.
Error común
Así no
SELECT
o.payment_method,
count(*) FILTER (WHERE o.status = 'cancelled') / count(*) AS tasa_cancelados
FROM orders AS o
GROUP BY o.payment_method;Dividir dos count(*). En PostgreSQL, count devuelve un entero y la división entre enteros descarta los decimales, así que la tasa da 0 en todos los grupos. Multiplicar primero por 100.0 convierte la cuenta a numeric. En cambio, sum(count(*)) ya devuelve numeric, por eso la participación no se trunca, pero conviene no depender de ese detalle.
En otros motores: PostgreSQL, SQL Server, MySQL, Oracle, BigQuery, Snowflake
PostgreSQLSQL Server
Entero dividido por entero da un entero:
25 / 145es0. Hay que convertir uno de los dos antes de dividir:100.0 *funciona en ambos motores; en SQL Server,CAST(x AS decimal(10, 2))con decimales explícitos.MySQLOracleBigQuerySnowflake
El operador
/devuelve un número con decimales aunque los dos valores sean enteros. La consulta que funcionaba ahí puede dar 0 al llevarla a PostgreSQL o SQL Server;100.0 *funciona igual en todos.BigQuery
SAFE_DIVIDE(a, b)devuelveNULLcuando el denominador es 0, igual quea / NULLIF(b, 0).
Estos fragmentos no se ejecutan en el curso: se muestran como referencia de cada motor.