ROUND(x, n) redondea el numero x a n decimales. Suena trivial, pero es justo aqui donde la gente tropieza: en PostgreSQL la forma de dos argumentos solo funciona con numeric, no con double precision, y la consulta falla de la nada.
Que hace ROUND(x, n)
El primer argumento es el numero, el segundo es cuantos decimales conservar. Redondea al valor mas cercano; los empates se alejan de cero para numeric en PostgreSQL.
SELECT
ROUND(3.14159, 2) AS pi2,
ROUND(3.14159, 0) AS pi0,
ROUND(2.5, 0) AS half;
Propiedades clave:
n vale 0 por defecto, asi que ROUND(x) redondea a un entero.
- El resultado conserva el tipo
numeric y la escala pedida: ROUND(2, 2) devuelve 2.00, no 2.
- Redondea, no trunca:
TRUNC(3.99, 1) da 3.9, mientras que ROUND(3.99, 1) da 4.0.
n negativo: redondear a la izquierda del punto decimal
La funcion mas infravalorada es un n negativo. Redondea a la izquierda del punto decimal: a decenas, centenas, millares. Resulta comodo para agrupar importes en cubos.
SELECT
ROUND(12345.678, -1) AS tens,
ROUND(12345.678, -2) AS hundreds,
ROUND(12345.678, -3) AS thousands;
En la practica te permite construir un histograma de importes de pedidos sin una tabla de rangos aparte:
SELECT
ROUND(amount, -2) AS bucket,
COUNT(*) AS orders_in_bucket
FROM orders
WHERE status = 'paid'
GROUP BY ROUND(amount, -2)
ORDER BY bucket;
Cada pedido cae en un cubo de ancho 100 y ves al instante la distribucion de los importes.
numeric frente a double precision: el gran tropiezo
Aqui esta el verdadero escollo. PostgreSQL tiene round(numeric, integer) pero no round(double precision, integer). Si la columna es double precision o real, la llamada de dos argumentos no encuentra una funcion compatible:
SELECT ROUND(salary / 12.0, 2) FROM employees;
Aqui salary / 12.0 produce double precision. La solucion es una conversion a numeric:
SELECT
name,
ROUND(salary::numeric / 12, 2) AS monthly_pay
FROM employees;
Lo desagradable es que la forma de un argumento ROUND(double precision) si existe y funciona, asi que el error queda oculto hasta que alguien anade un segundo argumento. Regla sencilla: guarda el dinero como numeric(12, 2) desde el principio y nunca necesitaras la conversion.
ROUND lleva el numero en si a la escala correcta, pero no anade un simbolo de moneda, no agrupa millares ni conserva ceros finales en un double precision. Para una salida bonita, usa to_char.
SELECT
u.name,
SUM(o.amount) AS raw_total,
ROUND(SUM(o.amount), 2) AS rounded_total,
to_char(ROUND(SUM(o.amount), 2), 'FM999G999D00') AS pretty_total
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE o.status = 'paid'
GROUP BY u.name;
La mascara FM999G999D00 quita los espacios de relleno (FM), pone un separador de millares (G) y fija dos decimales (D00). Redondea para el calculo y formatea con to_char para mostrar: no mezcles las dos tareas en una sola expresion.
Diferencias en MySQL y ClickHouse
La idea de ROUND(x, n) es la misma en todas partes, pero los detalles importan.
- MySQL acepta
ROUND tanto en tipos enteros como decimales sin conversion, y un n negativo tambien funciona. Para un formato de dinero con separadores, usa FORMAT(x, 2).
SELECT FORMAT(ROUND(amount, 2), 2) AS pretty_total
FROM orders;
- ClickHouse separa el redondeo bancario del habitual:
round redondea la mitad al par, mientras que roundBankers lo hace explicito. Si esperas el clasico "mitad hacia arriba", ten presente esta diferencia.
SELECT
round(2.5) AS r,
roundBankers(3.5) AS rb;
Recuerda una cosa: ROUND controla la precision de un numero, no su apariencia, y en PostgreSQL el segundo argumento casi siempre necesita numeric.
ROUND(x, n)redondea el numeroxandecimales. Suena trivial, pero es justo aqui donde la gente tropieza: en PostgreSQL la forma de dos argumentos solo funciona connumeric, no condouble precision, y la consulta falla de la nada.Que hace ROUND(x, n)
El primer argumento es el numero, el segundo es cuantos decimales conservar. Redondea al valor mas cercano; los empates se alejan de cero para
numericen PostgreSQL.SELECT ROUND(3.14159, 2) AS pi2, -- 3.14 ROUND(3.14159, 0) AS pi0, -- 3 ROUND(2.5, 0) AS half; -- 3Propiedades clave:
nvale0por defecto, asi queROUND(x)redondea a un entero.numericy la escala pedida:ROUND(2, 2)devuelve2.00, no2.TRUNC(3.99, 1)da3.9, mientras queROUND(3.99, 1)da4.0.n negativo: redondear a la izquierda del punto decimal
La funcion mas infravalorada es un
nnegativo. Redondea a la izquierda del punto decimal: a decenas, centenas, millares. Resulta comodo para agrupar importes en cubos.SELECT ROUND(12345.678, -1) AS tens, -- 12350 ROUND(12345.678, -2) AS hundreds, -- 12300 ROUND(12345.678, -3) AS thousands; -- 12000En la practica te permite construir un histograma de importes de pedidos sin una tabla de rangos aparte:
SELECT ROUND(amount, -2) AS bucket, COUNT(*) AS orders_in_bucket FROM orders WHERE status = 'paid' GROUP BY ROUND(amount, -2) ORDER BY bucket;Cada pedido cae en un cubo de ancho 100 y ves al instante la distribucion de los importes.
numeric frente a double precision: el gran tropiezo
Aqui esta el verdadero escollo. PostgreSQL tiene
round(numeric, integer)pero noround(double precision, integer). Si la columna esdouble precisionoreal, la llamada de dos argumentos no encuentra una funcion compatible:-- ERROR: function round(double precision, integer) does not exist SELECT ROUND(salary / 12.0, 2) FROM employees;Aqui
salary / 12.0producedouble precision. La solucion es una conversion anumeric:SELECT name, ROUND(salary::numeric / 12, 2) AS monthly_pay FROM employees;Lo desagradable es que la forma de un argumento
ROUND(double precision)si existe y funciona, asi que el error queda oculto hasta que alguien anade un segundo argumento. Regla sencilla: guarda el dinero comonumeric(12, 2)desde el principio y nunca necesitaras la conversion.Formatear moneda
ROUNDlleva el numero en si a la escala correcta, pero no anade un simbolo de moneda, no agrupa millares ni conserva ceros finales en undouble precision. Para una salida bonita, usato_char.SELECT u.name, SUM(o.amount) AS raw_total, ROUND(SUM(o.amount), 2) AS rounded_total, to_char(ROUND(SUM(o.amount), 2), 'FM999G999D00') AS pretty_total FROM users u JOIN orders o ON o.user_id = u.id WHERE o.status = 'paid' GROUP BY u.name;La mascara
FM999G999D00quita los espacios de relleno (FM), pone un separador de millares (G) y fija dos decimales (D00). Redondea para el calculo y formatea conto_charpara mostrar: no mezcles las dos tareas en una sola expresion.Diferencias en MySQL y ClickHouse
La idea de
ROUND(x, n)es la misma en todas partes, pero los detalles importan.ROUNDtanto en tipos enteros como decimales sin conversion, y unnnegativo tambien funciona. Para un formato de dinero con separadores, usaFORMAT(x, 2).SELECT FORMAT(ROUND(amount, 2), 2) AS pretty_total FROM orders;roundredondea la mitad al par, mientras queroundBankerslo hace explicito. Si esperas el clasico "mitad hacia arriba", ten presente esta diferencia.SELECT round(2.5) AS r, -- 2 (half to even) roundBankers(3.5) AS rb; -- 4Recuerda una cosa:
ROUNDcontrola la precision de un numero, no su apariencia, y en PostgreSQL el segundo argumento casi siempre necesitanumeric.