sqlpostgresqlroundnumeric

ROUND(x, n) en SQL: redondear a decimales, n negativo y formatear moneda

Como ROUND(x, n) redondea a n decimales, que hace un n negativo y por que PostgreSQL exige numeric en lugar de double precision en la forma de dos argumentos.

2 min de lecturaReferencesql · postgresql · round · numeric · currency · clickhouse

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,   -- 3.14
  ROUND(3.14159, 0) AS pi0,   -- 3
  ROUND(2.5,     0) AS half;  -- 3

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,       -- 12350
  ROUND(12345.678, -2) AS hundreds,   -- 12300
  ROUND(12345.678, -3) AS thousands;  -- 12000

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:

-- ERROR: function round(double precision, integer) does not exist
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.

Formatear moneda

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,   -- 2 (half to even)
  roundBankers(3.5) AS rb;  -- 4

Recuerda una cosa: ROUND controla la precision de un numero, no su apariencia, y en PostgreSQL el segundo argumento casi siempre necesita numeric.

Practica con ejercicios reales

Resuelve ejercicios en el entrenador de SQL con corrección instantánea y pistas.

Abrir el entrenador