Recipe

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 #

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 #

Loading SQL editor...

Common patterns #

sql
-- 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) and a % b both work. Result sign matches a (the dividend).
  • MySQL: MOD(a, b), a % b, and a MOD b all work. Same sign-of-dividend behavior.
  • SQL Server: % operator only. There's no MOD function until very recent versions. a % b.
  • Oracle: MOD(a, b), with no % operator. Sign of dividend.
  • Snowflake: MOD(a, b) and a % b. Sign of dividend.
  • BigQuery: MOD(a, b) only, with no % operator.
  • SQLite: % operator. MOD is not native (some builds add it as a function).
  • Floating-point modulo: most engines accept floats, so MOD(5.5, 2) returns 1.5. Older Oracle may require integer operands.
Tip

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).