MOD(a, b) devuelve el resto de dividir a entre b, el mismo valor que el operador %. Es la herramienta basica para comprobar la paridad, elegir cada N-esima fila, repartir filas en shards de forma uniforme por un identificador y cualquier logica ciclica.
Comportamiento basico
MOD(a, b) y a % b producen el mismo resultado: el resto que queda tras la division entera. El signo del resultado importa, y volveremos a ello mas abajo.
SELECT
MOD(10, 3) AS a,
10 % 3 AS b,
MOD(9, 3) AS c,
MOD(7, 10) AS d;
Puntos clave:
MOD(a, b) y a % b son intercambiables en PostgreSQL y MySQL; usa el que se lea mas claro.
- Si
a se divide exactamente entre b, el resto es 0. Esa es tu prueba de divisibilidad.
- Un divisor
0 es un error: MOD(5, 0) lanza division by zero, igual que la division normal.
Par/impar y cada N-esima fila
El resto entre 2 separa al instante las filas en pares e impares. Etiqueta los pedidos por la paridad de su id:
SELECT
id,
amount,
CASE WHEN MOD(id, 2) = 0 THEN 'even' ELSE 'odd' END AS parity
FROM orders
ORDER BY id;
El mismo truco escala a "cada N-esima fila". Para tomar aproximadamente uno de cada diez usuarios como muestra de control, filtra por el resto:
SELECT id, email, country
FROM users
WHERE MOD(id, 10) = 0
ORDER BY id;
Trampa. MOD(id, 10) = 0 solo funciona como muestreo si los valores de id son densos y estan espaciados de forma uniforme. Sobre un id secuencial es fiable, pero sobre UUIDs o secuencias con grandes huecos la muestra se sesga. Para un muestreo justo sobre texto, toma el resto de un hash, no del id en bruto.
Sharding por id % bucket
El resto es la forma natural de repartir filas en un numero fijo de buckets. Distribuye los usuarios en 4 shards segun su id:
SELECT
MOD(id, 4) AS shard,
COUNT(*) AS users
FROM users
GROUP BY MOD(id, 4)
ORDER BY shard;
Cada usuario cae siempre en el mismo shard 0..3, lo que resulta util para procesar por lotes o desplegar funciones por cohortes:
SELECT id, email
FROM users
WHERE MOD(id, 4) = 2
ORDER BY id;
Esto funciona porque, para id no negativos, el resultado de MOD(id, 4) siempre cae en 0..3. Los valores negativos se comportan distinto, lo vemos a continuacion.
La regla del signo y los negativos
La trampa principal de MOD: el signo del resultado sigue el signo del dividendo (el primer argumento), no del divisor. Es el comportamiento del estandar SQL en PostgreSQL y MySQL.
SELECT
MOD(-10, 3) AS a,
MOD(10, -3) AS b,
MOD(-10, -3) AS c;
Por eso, MOD(value, n) = 0 para paridad y divisibilidad es seguro, pero MOD(value, n) como indice de bucket no lo es: para un value negativo da un bucket negativo. Para obtener siempre un resto no negativo, suma el divisor y vuelve a tomar el resto:
SELECT MOD(MOD(-10, 3) + 3, 3) AS bucket;
Recuerda tambien NULL: si cualquiera de los argumentos es NULL, el resultado es NULL, no 0. Tenlo en cuenta en WHERE y GROUP BY.
Diferencias entre bases de datos
- PostgreSQL y MySQL: ambos admiten
MOD(a, b) y a % b con la misma regla de signo (por el dividendo).
- PostgreSQL:
% funciona con enteros y numeric. Para tipos decimales existe MOD(numeric, numeric), por ejemplo MOD(5.5, 2) es 1.5.
- ClickHouse: usa
modulo(a, b) o el operador %; tambien existe moduloOrZero(a, b), que devuelve 0 en vez de fallar con un divisor cero, y positiveModulo(a, b) para un resultado siempre no negativo.
SELECT
moduloOrZero(10, 0) AS a,
positiveModulo(-10, 3) AS b;
MOD(a, b)devuelve el resto de dividiraentreb, el mismo valor que el operador%. Es la herramienta basica para comprobar la paridad, elegir cada N-esima fila, repartir filas en shards de forma uniforme por un identificador y cualquier logica ciclica.Comportamiento basico
MOD(a, b)ya % bproducen el mismo resultado: el resto que queda tras la division entera. El signo del resultado importa, y volveremos a ello mas abajo.SELECT MOD(10, 3) AS a, -- 1 10 % 3 AS b, -- 1 MOD(9, 3) AS c, -- 0 MOD(7, 10) AS d; -- 7Puntos clave:
MOD(a, b)ya % bson intercambiables en PostgreSQL y MySQL; usa el que se lea mas claro.ase divide exactamente entreb, el resto es0. Esa es tu prueba de divisibilidad.0es un error:MOD(5, 0)lanzadivision by zero, igual que la division normal.Par/impar y cada N-esima fila
El resto entre
2separa al instante las filas en pares e impares. Etiqueta los pedidos por la paridad de suid:SELECT id, amount, CASE WHEN MOD(id, 2) = 0 THEN 'even' ELSE 'odd' END AS parity FROM orders ORDER BY id;El mismo truco escala a "cada N-esima fila". Para tomar aproximadamente uno de cada diez usuarios como muestra de control, filtra por el resto:
SELECT id, email, country FROM users WHERE MOD(id, 10) = 0 ORDER BY id;Trampa.
MOD(id, 10) = 0solo funciona como muestreo si los valores deidson densos y estan espaciados de forma uniforme. Sobre unidsecuencial es fiable, pero sobre UUIDs o secuencias con grandes huecos la muestra se sesga. Para un muestreo justo sobre texto, toma el resto de un hash, no deliden bruto.Sharding por id % bucket
El resto es la forma natural de repartir filas en un numero fijo de buckets. Distribuye los usuarios en 4 shards segun su
id:SELECT MOD(id, 4) AS shard, COUNT(*) AS users FROM users GROUP BY MOD(id, 4) ORDER BY shard;Cada usuario cae siempre en el mismo shard
0..3, lo que resulta util para procesar por lotes o desplegar funciones por cohortes:SELECT id, email FROM users WHERE MOD(id, 4) = 2 -- process only shard 2 in this batch ORDER BY id;Esto funciona porque, para
idno negativos, el resultado deMOD(id, 4)siempre cae en0..3. Los valores negativos se comportan distinto, lo vemos a continuacion.La regla del signo y los negativos
La trampa principal de
MOD: el signo del resultado sigue el signo del dividendo (el primer argumento), no del divisor. Es el comportamiento del estandar SQL en PostgreSQL y MySQL.SELECT MOD(-10, 3) AS a, -- -1 MOD(10, -3) AS b, -- 1 MOD(-10, -3) AS c; -- -1Por eso,
MOD(value, n) = 0para paridad y divisibilidad es seguro, peroMOD(value, n)como indice de bucket no lo es: para unvaluenegativo da un bucket negativo. Para obtener siempre un resto no negativo, suma el divisor y vuelve a tomar el resto:-- always lands in 0..(n-1), even for negative input SELECT MOD(MOD(-10, 3) + 3, 3) AS bucket; -- 2Recuerda tambien
NULL: si cualquiera de los argumentos esNULL, el resultado esNULL, no0. Tenlo en cuenta enWHEREyGROUP BY.Diferencias entre bases de datos
MOD(a, b)ya % bcon la misma regla de signo (por el dividendo).%funciona con enteros ynumeric. Para tipos decimales existeMOD(numeric, numeric), por ejemploMOD(5.5, 2)es1.5.modulo(a, b)o el operador%; tambien existemoduloOrZero(a, b), que devuelve0en vez de fallar con un divisor cero, ypositiveModulo(a, b)para un resultado siempre no negativo.-- ClickHouse: safe modulo and always-positive modulo SELECT moduloOrZero(10, 0) AS a, -- 0, no error positiveModulo(-10, 3) AS b; -- 2