MOD(a, b) возвращает остаток от деления a на b.
Проще говоря: SQL делит одно число на другое, берёт целую часть, а всё, что «не поместилось», возвращает как остаток.
Например, 10 не делится на 3 нацело:
- 3 помещается в 10 три раза;
- 3 × 3 = 9;
- остаётся 1.
Значит, MOD(10, 3) вернёт 1.
В SQL этот приём используют очень часто: проверить чётность числа, найти каждую десятую строку, разложить пользователей по корзинам, распределить данные по шардам или построить любую циклическую логику.
Базовый пример
В PostgreSQL и MySQL можно писать и функцию MOD(a, b), и оператор a % b. Обычно они означают одно и то же: остаток от деления.
SELECT
MOD(10, 3) AS a,
10 % 3 AS b,
MOD(9, 3) AS c,
MOD(7, 10) AS d;
Результат будет таким:
Разберём каждое значение:
MOD(10, 3) возвращает 1, потому что 10 делится на 3 с остатком 1.
10 % 3 тоже возвращает 1. Это просто другой синтаксис для той же идеи.
MOD(9, 3) возвращает 0, потому что 9 делится на 3 нацело.
MOD(7, 10) возвращает 7, потому что 10 ни разу не помещается в 7, и весь остаток — это само число 7.
Проверка делимости
Самое важное правило:
если число делится на другое число без остатка, MOD возвращает 0.
Например, заказ с количеством товаров 12 можно ровно разложить по коробкам по 4 товара:
SELECT MOD(12, 4) AS remainder;
Результат:
А вот 14 товаров по коробкам по 4 уже не раскладываются идеально:
SELECT MOD(14, 4) AS remainder;
Результат:
Осталось 2 товара.
Именно поэтому MOD удобен для проверок вида «делится или не делится»:
SELECT id, amount
FROM orders
WHERE MOD(amount, 1000) = 0;
Такой запрос найдёт заказы, у которых сумма ровно кратна 1000.
Деление на ноль
С делителем нужно быть осторожным.
SELECT MOD(5, 0) AS result;
Так делать нельзя. Деление на ноль вызовет ошибку.
По смыслу это логично: нельзя понять, какой остаток получится при делении на ноль, потому что самого деления не существует.
В реальных запросах это особенно важно, когда делитель приходит из столбца:
SELECT
value,
divider,
MOD(value, divider) AS remainder
FROM numbers;
Если в divider окажется 0, запрос упадёт с ошибкой. Поэтому перед такими вычислениями делитель часто проверяют:
SELECT
value,
divider,
MOD(value, divider) AS remainder
FROM numbers
WHERE divider <> 0;
Как проверить чётность числа
Один из самых популярных сценариев для MOD — проверка чётности.
Чётное число делится на 2 без остатка. Значит:
- если
MOD(id, 2) = 0, число чётное;
- если
MOD(id, 2) = 1, число нечётное.
Например, пометим заказы по чётности id:
SELECT
id,
amount,
CASE
WHEN MOD(id, 2) = 0 THEN 'even'
ELSE 'odd'
END AS parity
FROM orders
ORDER BY id;
Результат может быть таким:
| id |
amount |
parity |
| 1 |
1200 |
odd |
| 2 |
850 |
even |
| 3 |
430 |
odd |
| 4 |
2100 |
even |
Для новичка это хороший способ почувствовать MOD: мы не «угадываем» чётность, а задаём SQL простой математический вопрос — есть ли остаток при делении на 2.
Как выбрать каждую N-ю строку
MOD удобно использовать, когда нужно взять каждую N-ю запись.
Например, выбрать каждого десятого пользователя:
SELECT id, email, country
FROM users
WHERE MOD(id, 10) = 0
ORDER BY id;
Такой запрос вернёт пользователей с id, которые делятся на 10 без остатка:
Это похоже на простую выборку для проверки: не все пользователи, а примерно каждый десятый.
Но здесь есть важный нюанс.
Такой подход хорошо работает, если id идут более-менее равномерно: 1, 2, 3, 4, 5 и так далее. Если в последовательности много дыр или идентификаторы устроены сложно, выборка может получиться кривой.
Например, если в таблице остались только такие id:
то условие MOD(id, 10) = 0 выберет вообще всех. Это уже не «примерно каждый десятый», а просто вся таблица.
А если используются UUID, делить сам идентификатор через MOD обычно нельзя напрямую. В таких случаях берут хэш от значения, а уже потом считают остаток от хэша.
Корзины и шардинг через MOD
Ещё один полезный сценарий — разложить строки по корзинам.
Представьте, что у нас есть пользователи, и мы хотим распределить их по 4 группам. Например, чтобы обрабатывать данные частями или постепенно включать новую фичу.
Для этого можно взять остаток от деления id на 4:
SELECT
MOD(id, 4) AS bucket,
COUNT(*) AS users
FROM users
GROUP BY MOD(id, 4)
ORDER BY bucket;
Результат может быть таким:
| bucket |
users |
| 0 |
250 |
| 1 |
249 |
| 2 |
251 |
| 3 |
250 |
Что здесь происходит?
MOD(id, 4) всегда возвращает один из четырёх остатков:
То есть каждый пользователь попадает в одну из четырёх корзин.
Например:
| id |
MOD(id, 4) |
| 1 |
1 |
| 2 |
2 |
| 3 |
3 |
| 4 |
0 |
| 5 |
1 |
| 6 |
2 |
Так можно стабильно делить пользователей на группы. Пользователь с одним и тем же id всегда попадёт в одну и ту же корзину.
Например, можно обработать только одну корзину:
SELECT id, email
FROM users
WHERE MOD(id, 4) = 2
ORDER BY id;
Такой запрос выберет пользователей из корзины 2.
Этот приём часто используют для пакетной обработки: сегодня обработали корзину 0, потом 1, потом 2, потом 3.
Важная ловушка: отрицательные числа
С положительными числами всё обычно понятно. Но с отрицательными есть ловушка.
Во многих случаях результат MOD сохраняет знак первого аргумента, то есть делимого.
Посмотрим на пример:
SELECT
MOD(-10, 3) AS a,
MOD(10, -3) AS b,
MOD(-10, -3) AS c;
Результат:
Главная мысль: знак результата зависит от первого аргумента.
Это не страшно, если вы проверяете делимость:
SELECT MOD(-10, 2) AS remainder;
Результат будет 0, потому что -10 делится на 2 без остатка.
Но это может быть проблемой, если вы используете MOD как номер корзины.
Например:
SELECT MOD(-10, 3) AS bucket;
Результат:
А корзина -1 нам обычно не нужна. Если мы хотели получить одну из корзин 0, 1, 2, отрицательное значение ломает идею.
Как всегда получить неотрицательный остаток
Чтобы гарантированно получить остаток в диапазоне от 0 до n - 1, можно применить такой приём:
SELECT MOD(MOD(-10, 3) + 3, 3) AS bucket;
Результат:
Что здесь происходит:
сначала MOD(-10, 3) даёт -1.
Потом мы прибавляем делитель:
-1 + 3
Получается 2.
Потом ещё раз берём остаток от деления на 3. В итоге значение аккуратно попадает в диапазон 0..2.
В общем виде формула выглядит так:
SELECT MOD(MOD(value, n) + n, n) AS bucket
FROM numbers;
Она полезна, когда value может быть отрицательным, а вам нужен нормальный номер корзины.
Что будет с NULL
MOD не превращает NULL в ноль.
Если хотя бы один аргумент равен NULL, результат тоже будет NULL.
SELECT
MOD(NULL, 3) AS a,
MOD(10, NULL) AS b;
Результат:
Это стандартное поведение SQL: если значение неизвестно, результат вычисления тоже неизвестен.
Поэтому такой запрос не найдёт строки с NULL:
SELECT id, value
FROM numbers
WHERE MOD(value, 2) = 0;
Если value равен NULL, выражение MOD(value, 2) = 0 не станет истинным. Оно даст неизвестный результат, а WHERE пропускает только строки, где условие истинно.
Если нужно отдельно обработать NULL, добавьте явную проверку:
SELECT id, value
FROM numbers
WHERE value IS NULL OR MOD(value, 2) = 0;
MOD в разных СУБД
В PostgreSQL и MySQL обычно можно использовать оба варианта:
SELECT MOD(10, 3) AS a;
Или так:
SELECT 10 % 3 AS a;
Оба варианта возвращают остаток от деления.
В PostgreSQL оператор % работает с целыми числами и с типом numeric. Для дробных значений можно использовать MOD.
Например:
SELECT MOD(5.5, 2) AS remainder;
Результат:
В ClickHouse тоже есть оператор %, а ещё есть специальные функции.
Например, moduloOrZero возвращает 0, если делитель равен нулю:
SELECT moduloOrZero(10, 0) AS result;
А positiveModulo помогает получить неотрицательный остаток:
SELECT positiveModulo(-10, 3) AS bucket;
Результат:
Это удобно, когда вы заранее знаете, что работаете с ClickHouse и хотите явно показать намерение: не просто взять остаток, а получить безопасный или положительный вариант.
Когда использовать MOD
MOD особенно полезен, когда в задаче есть повторяемость, циклы или деление на группы.
Например:
- проверить, чётный или нечётный
id;
- найти значения, которые делятся на число без остатка;
- выбрать каждую десятую строку;
- разложить пользователей по корзинам;
- обрабатывать данные частями;
- распределить пользователей по шардам;
- включать фичу только для части аудитории.
Главное — помнить три правила.
Первое: если остаток равен 0, значит число делится нацело.
Второе: деление на ноль вызовет ошибку.
Третье: с отрицательными числами результат может быть отрицательным, поэтому для корзин лучше нормализовать остаток.
Короткий пример из жизни
Допустим, у нас есть таблица пользователей, и мы хотим включить новую фичу только для четверти аудитории.
Можно выбрать одну корзину из четырёх:
SELECT id, email
FROM users
WHERE MOD(id, 4) = 0
ORDER BY id;
Если пользователей много и id распределены равномерно, это даст примерно 25% пользователей.
Потом можно включить ещё одну корзину:
SELECT id, email
FROM users
WHERE MOD(id, 4) IN (0, 1)
ORDER BY id;
Теперь получится примерно половина аудитории.
А потом можно включить все корзины:
SELECT id, email
FROM users
WHERE MOD(id, 4) IN (0, 1, 2, 3)
ORDER BY id;
Так MOD превращается из простой математической функции в удобный инструмент для управляемого запуска изменений.
Главное из статьи
MOD(a, b) возвращает остаток от деления a на b.
Если результат равен 0, значит a делится на b нацело.
Для проверки чётности используют деление на 2:
SELECT id
FROM orders
WHERE MOD(id, 2) = 0;
Для выборки каждой N-й строки можно использовать деление на N:
SELECT id, email
FROM users
WHERE MOD(id, 10) = 0;
Для корзин и шардов удобно брать остаток от количества корзин:
SELECT MOD(id, 4) AS bucket
FROM users;
Но если исходное значение может быть отрицательным, обычный MOD может вернуть отрицательный остаток. Тогда результат лучше нормализовать:
SELECT MOD(MOD(value, n) + n, n) AS bucket
FROM numbers;
MOD — маленькая функция, но очень практичная. Она помогает SQL-запросам отвечать не только на вопрос «сколько?», но и на вопрос «в какую группу это попадает?».
MOD(a, b)возвращает остаток от деленияaнаb.Проще говоря: SQL делит одно число на другое, берёт целую часть, а всё, что «не поместилось», возвращает как остаток.
Например, 10 не делится на 3 нацело:
Значит,
MOD(10, 3)вернёт1.В SQL этот приём используют очень часто: проверить чётность числа, найти каждую десятую строку, разложить пользователей по корзинам, распределить данные по шардам или построить любую циклическую логику.
Базовый пример
В PostgreSQL и MySQL можно писать и функцию
MOD(a, b), и операторa % b. Обычно они означают одно и то же: остаток от деления.SELECT MOD(10, 3) AS a, 10 % 3 AS b, MOD(9, 3) AS c, MOD(7, 10) AS d;Результат будет таким:
Разберём каждое значение:
MOD(10, 3)возвращает1, потому что 10 делится на 3 с остатком 1.10 % 3тоже возвращает1. Это просто другой синтаксис для той же идеи.MOD(9, 3)возвращает0, потому что 9 делится на 3 нацело.MOD(7, 10)возвращает7, потому что 10 ни разу не помещается в 7, и весь остаток — это само число 7.Проверка делимости
Самое важное правило:
если число делится на другое число без остатка,
MODвозвращает0.Например, заказ с количеством товаров 12 можно ровно разложить по коробкам по 4 товара:
SELECT MOD(12, 4) AS remainder;Результат:
А вот 14 товаров по коробкам по 4 уже не раскладываются идеально:
SELECT MOD(14, 4) AS remainder;Результат:
Осталось 2 товара.
Именно поэтому
MODудобен для проверок вида «делится или не делится»:SELECT id, amount FROM orders WHERE MOD(amount, 1000) = 0;Такой запрос найдёт заказы, у которых сумма ровно кратна 1000.
Деление на ноль
С делителем нужно быть осторожным.
SELECT MOD(5, 0) AS result;Так делать нельзя. Деление на ноль вызовет ошибку.
По смыслу это логично: нельзя понять, какой остаток получится при делении на ноль, потому что самого деления не существует.
В реальных запросах это особенно важно, когда делитель приходит из столбца:
SELECT value, divider, MOD(value, divider) AS remainder FROM numbers;Если в
dividerокажется0, запрос упадёт с ошибкой. Поэтому перед такими вычислениями делитель часто проверяют:SELECT value, divider, MOD(value, divider) AS remainder FROM numbers WHERE divider <> 0;Как проверить чётность числа
Один из самых популярных сценариев для
MOD— проверка чётности.Чётное число делится на 2 без остатка. Значит:
MOD(id, 2) = 0, число чётное;MOD(id, 2) = 1, число нечётное.Например, пометим заказы по чётности
id:SELECT id, amount, CASE WHEN MOD(id, 2) = 0 THEN 'even' ELSE 'odd' END AS parity FROM orders ORDER BY id;Результат может быть таким:
Для новичка это хороший способ почувствовать
MOD: мы не «угадываем» чётность, а задаём SQL простой математический вопрос — есть ли остаток при делении на 2.Как выбрать каждую N-ю строку
MODудобно использовать, когда нужно взять каждую N-ю запись.Например, выбрать каждого десятого пользователя:
SELECT id, email, country FROM users WHERE MOD(id, 10) = 0 ORDER BY id;Такой запрос вернёт пользователей с
id, которые делятся на 10 без остатка:Это похоже на простую выборку для проверки: не все пользователи, а примерно каждый десятый.
Но здесь есть важный нюанс.
Такой подход хорошо работает, если
idидут более-менее равномерно: 1, 2, 3, 4, 5 и так далее. Если в последовательности много дыр или идентификаторы устроены сложно, выборка может получиться кривой.Например, если в таблице остались только такие
id:то условие
MOD(id, 10) = 0выберет вообще всех. Это уже не «примерно каждый десятый», а просто вся таблица.А если используются UUID, делить сам идентификатор через
MODобычно нельзя напрямую. В таких случаях берут хэш от значения, а уже потом считают остаток от хэша.Корзины и шардинг через MOD
Ещё один полезный сценарий — разложить строки по корзинам.
Представьте, что у нас есть пользователи, и мы хотим распределить их по 4 группам. Например, чтобы обрабатывать данные частями или постепенно включать новую фичу.
Для этого можно взять остаток от деления
idна 4:SELECT MOD(id, 4) AS bucket, COUNT(*) AS users FROM users GROUP BY MOD(id, 4) ORDER BY bucket;Результат может быть таким:
Что здесь происходит?
MOD(id, 4)всегда возвращает один из четырёх остатков:0;1;2;3.То есть каждый пользователь попадает в одну из четырёх корзин.
Например:
Так можно стабильно делить пользователей на группы. Пользователь с одним и тем же
idвсегда попадёт в одну и ту же корзину.Например, можно обработать только одну корзину:
SELECT id, email FROM users WHERE MOD(id, 4) = 2 ORDER BY id;Такой запрос выберет пользователей из корзины
2.Этот приём часто используют для пакетной обработки: сегодня обработали корзину
0, потом1, потом2, потом3.Важная ловушка: отрицательные числа
С положительными числами всё обычно понятно. Но с отрицательными есть ловушка.
Во многих случаях результат
MODсохраняет знак первого аргумента, то есть делимого.Посмотрим на пример:
SELECT MOD(-10, 3) AS a, MOD(10, -3) AS b, MOD(-10, -3) AS c;Результат:
Главная мысль: знак результата зависит от первого аргумента.
Это не страшно, если вы проверяете делимость:
SELECT MOD(-10, 2) AS remainder;Результат будет
0, потому что-10делится на 2 без остатка.Но это может быть проблемой, если вы используете
MODкак номер корзины.Например:
SELECT MOD(-10, 3) AS bucket;Результат:
А корзина
-1нам обычно не нужна. Если мы хотели получить одну из корзин0,1,2, отрицательное значение ломает идею.Как всегда получить неотрицательный остаток
Чтобы гарантированно получить остаток в диапазоне от
0доn - 1, можно применить такой приём:SELECT MOD(MOD(-10, 3) + 3, 3) AS bucket;Результат:
Что здесь происходит:
сначала
MOD(-10, 3)даёт-1.Потом мы прибавляем делитель:
-1 + 3Получается
2.Потом ещё раз берём остаток от деления на 3. В итоге значение аккуратно попадает в диапазон
0..2.В общем виде формула выглядит так:
SELECT MOD(MOD(value, n) + n, n) AS bucket FROM numbers;Она полезна, когда
valueможет быть отрицательным, а вам нужен нормальный номер корзины.Что будет с NULL
MODне превращаетNULLв ноль.Если хотя бы один аргумент равен
NULL, результат тоже будетNULL.SELECT MOD(NULL, 3) AS a, MOD(10, NULL) AS b;Результат:
Это стандартное поведение SQL: если значение неизвестно, результат вычисления тоже неизвестен.
Поэтому такой запрос не найдёт строки с
NULL:SELECT id, value FROM numbers WHERE MOD(value, 2) = 0;Если
valueравенNULL, выражениеMOD(value, 2) = 0не станет истинным. Оно даст неизвестный результат, аWHEREпропускает только строки, где условие истинно.Если нужно отдельно обработать
NULL, добавьте явную проверку:SELECT id, value FROM numbers WHERE value IS NULL OR MOD(value, 2) = 0;MOD в разных СУБД
В PostgreSQL и MySQL обычно можно использовать оба варианта:
SELECT MOD(10, 3) AS a;Или так:
SELECT 10 % 3 AS a;Оба варианта возвращают остаток от деления.
В PostgreSQL оператор
%работает с целыми числами и с типомnumeric. Для дробных значений можно использоватьMOD.Например:
SELECT MOD(5.5, 2) AS remainder;Результат:
В ClickHouse тоже есть оператор
%, а ещё есть специальные функции.Например,
moduloOrZeroвозвращает0, если делитель равен нулю:SELECT moduloOrZero(10, 0) AS result;А
positiveModuloпомогает получить неотрицательный остаток:SELECT positiveModulo(-10, 3) AS bucket;Результат:
Это удобно, когда вы заранее знаете, что работаете с ClickHouse и хотите явно показать намерение: не просто взять остаток, а получить безопасный или положительный вариант.
Когда использовать MOD
MODособенно полезен, когда в задаче есть повторяемость, циклы или деление на группы.Например:
id;Главное — помнить три правила.
Первое: если остаток равен
0, значит число делится нацело.Второе: деление на ноль вызовет ошибку.
Третье: с отрицательными числами результат может быть отрицательным, поэтому для корзин лучше нормализовать остаток.
Короткий пример из жизни
Допустим, у нас есть таблица пользователей, и мы хотим включить новую фичу только для четверти аудитории.
Можно выбрать одну корзину из четырёх:
SELECT id, email FROM users WHERE MOD(id, 4) = 0 ORDER BY id;Если пользователей много и
idраспределены равномерно, это даст примерно 25% пользователей.Потом можно включить ещё одну корзину:
SELECT id, email FROM users WHERE MOD(id, 4) IN (0, 1) ORDER BY id;Теперь получится примерно половина аудитории.
А потом можно включить все корзины:
SELECT id, email FROM users WHERE MOD(id, 4) IN (0, 1, 2, 3) ORDER BY id;Так
MODпревращается из простой математической функции в удобный инструмент для управляемого запуска изменений.Главное из статьи
MOD(a, b)возвращает остаток от деленияaнаb.Если результат равен
0, значитaделится наbнацело.Для проверки чётности используют деление на 2:
SELECT id FROM orders WHERE MOD(id, 2) = 0;Для выборки каждой N-й строки можно использовать деление на N:
SELECT id, email FROM users WHERE MOD(id, 10) = 0;Для корзин и шардов удобно брать остаток от количества корзин:
SELECT MOD(id, 4) AS bucket FROM users;Но если исходное значение может быть отрицательным, обычный
MODможет вернуть отрицательный остаток. Тогда результат лучше нормализовать:SELECT MOD(MOD(value, n) + n, n) AS bucket FROM numbers;MOD— маленькая функция, но очень практичная. Она помогает SQL-запросам отвечать не только на вопрос «сколько?», но и на вопрос «в какую группу это попадает?».