sqlpostgresqlmysqlclickhouse

CEIL / CEILING in SQL: Rounding Up to the Next Integer

How CEIL/CEILING rounds up, computes pagination page counts and buckets, and how it differs from FLOOR and ROUND.

2 min čteníReferencesql · postgresql · mysql · clickhouse · math · pagination
Tento článek je momentálně v ruštině — anglický překlad se připravuje.

CEIL (он же CEILING) округляет число вверх до ближайшего целого, которое не меньше исходного значения. Это рабочая лошадка для подсчёта страниц пагинации, разбиения данных по корзинам и любой логики «сколько контейнеров нужно, чтобы вместить N штук».

Особенность функции в том, что она кодирует не математическую красоту, а инженерное ограничение: остаток нельзя разрезать пополам и положить на ту же страницу, в тот же батч или в ту же коробку.

Базовое поведение

CEIL всегда движется в сторону плюс бесконечности. Целое число остаётся как есть; любая дробная часть тянет результат вверх.

SELECT
  CEIL(4.1)  AS a,   -- 5
  CEIL(4.0)  AS b,   -- 4
  CEIL(-4.1) AS c,   -- -4  (toward zero, not away)
  CEILING(7.0001) AS d; -- 8

Ключевые моменты:

  • CEIL и CEILING полностью идентичны во всех основных СУБД — это синонимы.
  • Для отрицательных чисел «вверх» означает в сторону нуля: CEIL(-4.1) равно -4, а не -5.
  • В PostgreSQL результат над numeric остаётся типом numeric, а над double precisiondouble precision. Сама дробная часть всегда обнуляется.

Подсчёт страниц пагинации

Классический случай: есть total строк и per_page на страницу — сколько всего страниц? Целочисленное деление обрежет «хвост», поэтому нужен CEIL.

SELECT
  COUNT(*) AS total_orders,
  CEIL(COUNT(*)::numeric / 25) AS total_pages
FROM orders
WHERE status = 'paid';

Тот же приём для каждой страны — сколько экранов писем нужно, чтобы показать всех пользователей по 20 на страницу:

SELECT
  country,
  COUNT(*) AS users,
  CEIL(COUNT(*)::numeric / 20) AS pages
FROM users
GROUP BY country
ORDER BY users DESC;

Подводный камень — целочисленное деление. В PostgreSQL 5 / 2 равно 2 (integer), и CEIL(5 / 2) вернёт 2, а не 3. Деление уже произошло до вызова CEIL. Всегда приводите хотя бы один операнд к дробному типу: CEIL(5::numeric / 2) или CEIL(5.0 / 2). Эта ошибка особенно коварна, потому что на «ровных» данных (когда total кратен per_page) результат совпадает с правильным, и баг всплывает только на последней неполной странице.

Бакетинг и разбиение по диапазонам

CEIL отлично раскладывает непрерывные значения по дискретным корзинам фиксированной ширины. Разложим сотрудников по зарплатным «полкам» шириной 10000:

SELECT
  CEIL(salary / 10000.0) * 10000 AS salary_band,
  COUNT(*) AS people
FROM employees
GROUP BY CEIL(salary / 10000.0)
ORDER BY salary_band;

Сотрудник с зарплатой 41000 попадёт в полку 50000 (верхняя граница диапазона 40001–50000). Если нужна нижняя граница, используйте FLOOR. Аналогично можно резать суммы заказов на ценовые сегменты:

SELECT
  CEIL(amount / 100.0) AS price_bucket,
  COUNT(*) AS orders
FROM orders
GROUP BY CEIL(amount / 100.0)
ORDER BY price_bucket;

CEIL против FLOOR и ROUND

Три функции, три разных направления:

  • CEIL(x) — вверх, к плюс бесконечности: CEIL(2.1) = 3, CEIL(2.9) = 3.
  • FLOOR(x) — вниз, к минус бесконечности: FLOOR(2.9) = 2, FLOOR(-2.1) = -3.
  • ROUND(x) — к ближайшему целому: ROUND(2.4) = 2, ROUND(2.5) = 3.
SELECT
  x,
  CEIL(x)  AS up,
  FLOOR(x) AS down,
  ROUND(x) AS nearest
FROM (VALUES (2.1), (2.5), (2.9), (-2.5)) AS t(x);

Для пагинации и «сколько контейнеров нужно» берите только CEIL: даже одна лишняя строка требует ещё одну целую страницу, а ROUND ошибётся ровно на половине.

Различия между СУБД

  • PostgreSQL и MySQL: CEIL и CEILING работают одинаково; в MySQL тоже остерегайтесь целочисленного деления — используйте / с дробными литералами или функцию.
  • ClickHouse: функция называется ceil и принимает второй аргумент — точность: ceil(4.123, 1) равно 4.2. Это удобно для округления вверх до десятых или сотых, чего нет в стандартном CEIL.
  • Во всех системах CEIL(NULL) возвращает NULL — учитывайте это в агрегатах и COALESCE.
-- ClickHouse: round up to one decimal place
SELECT ceil(4.123, 1) AS r;  -- 4.2

Итог: CEIL выбирают не потому, что он «округляет красиво», а потому что даже малый остаток требует ещё одну целую единицу: страницу, коробку, батч или верхнюю границу диапазона. Проверьте только тип деления — если оно целочисленное, правильная идея даст неправильный ответ.

Procvičujte na reálných úlohách

Řešte úlohy v SQL trenéru s okamžitým hodnocením a nápovědami.

Otevřít trenéra