Modulo and remainder
Short answer: MOD(a, b) returns the remainder of a / b on most engines. Postgres / MySQL / DuckDB / SQL Server / SQLite also accept the % operator. The result has the sign of a on most engines (so -5 MOD 3 is -2, not 1). Use it for: hashing rows into buckets, "every Nth row" sampling, modular arithmetic on counters, and detecting odd/even values.
Canonical SQL #
-- Function form (works on every major engine).
SELECT MOD(10, 3); -- 1
SELECT MOD(11, 3); -- 2
SELECT MOD(12, 3); -- 0
-- Operator form (Postgres / MySQL / DuckDB / SQL Server / SQLite).
SELECT 10 % 3; -- 1
-- Even / odd test.
SELECT id FROM users WHERE MOD(id, 2) = 0; -- even ids
SELECT id FROM users WHERE id % 2 = 1; -- odd ids
-- Bucketing: distribute rows into N evenly-sized buckets by hash.
SELECT id, MOD(ABS(HASH(id)), 10) AS bucket FROM users;Example #
Common patterns #
-- Sample 1% of rows reproducibly (no RANDOM — same rows every run).
SELECT * FROM ecom_events WHERE MOD(ABS(HASH(event_id)), 100) = 0;
-- Bucket users for an A/B test (50/50 split).
SELECT
id AS user_id,
CASE WHEN MOD(ABS(HASH(id)), 2) = 0 THEN 'control' ELSE 'treatment' END AS variant
FROM users;
-- "Is this minute a 5-minute boundary?"
SELECT * FROM ecom_events WHERE MOD(EXTRACT(MINUTE FROM event_timestamp), 5) = 0;
-- Wrap a counter (e.g., position 12 in a wheel of 8 → position 4).
WITH rotations(pos, wheel_size) AS (VALUES (12, 8), (20, 8))
SELECT MOD(pos, wheel_size) AS slot FROM rotations;
-- Detect divisibility (FizzBuzz-style logic).
SELECT
n,
CASE
WHEN MOD(n, 15) = 0 THEN 'FizzBuzz'
WHEN MOD(n, 3) = 0 THEN 'Fizz'
WHEN MOD(n, 5) = 0 THEN 'Buzz'
ELSE n::TEXT
END AS label
FROM range(1, 21) AS r(n);Dialect notes #
- Postgres / DuckDB:
MOD(a, b)anda % bboth work. Result sign matchesa(the dividend). - MySQL:
MOD(a, b),a % b, anda MOD ball work. Same sign-of-dividend behavior. - SQL Server:
%operator only. There's noMODfunction until very recent versions.a % b. - Oracle:
MOD(a, b), with no%operator. Sign of dividend. - Snowflake:
MOD(a, b)anda % b. Sign of dividend. - BigQuery:
MOD(a, b)only, with no%operator. - SQLite:
%operator.MODis not native (some builds add it as a function). - Floating-point modulo: most engines accept floats, so
MOD(5.5, 2)returns1.5. Older Oracle may require integer operands.
Modulo + hash = stable, reproducible bucketing. MOD(ABS(HASH(user_id)), 100) assigns each user to one of 100 buckets in a way that's deterministic (same user → same bucket forever) and uniformly distributed. The pattern under-the-hood for almost every A/B test, sampling job, and shard-by-user strategy. Safer than RANDOM() because it doesn't change between runs; safer than MOD(user_id, 100) directly because hashing prevents bias when user IDs aren't random (e.g., sequential IDs would put neighboring signups in the same bucket).