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 de cititReferencesql · postgresql · mysql · clickhouse · math · pagination
Acest articol este momentan în limba rusă — traducerea în engleză este în curs.

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 выбирают не потому, что он «округляет красиво», а потому что даже малый остаток требует ещё одну целую единицу: страницу, коробку, батч или верхнюю границу диапазона. Проверьте только тип деления — если оно целочисленное, правильная идея даст неправильный ответ.

Exersează pe probleme reale

Rezolvă probleme în antrenorul SQL cu notare instantanee și indicii.

Deschide antrenorul