CEIL округляет число вверх до ближайшего целого.
У функции есть второе имя — CEILING. В большинстве популярных СУБД они означают одно и то же.
Главная идея простая: если после деления остался хоть маленький хвостик, нужна ещё одна целая единица.
Например:
- 101 товар по коробкам на 100 штук — это 2 коробки;
- 26 записей по 25 на страницу — это 2 страницы;
- 41000 зарплаты при шаге 10000 — это верхняя полка 50000.
CEIL — это не про красивое округление. Это про инженерную логику: нельзя положить половину строки на страницу, половину заказа в батч или кусочек товара в несуществующую коробку.
Что делает CEIL
CEIL(x) возвращает ближайшее целое число, которое не меньше x.
Проще говоря, функция округляет число вверх.
SELECT
CEIL(4.1) AS a,
CEIL(4.0) AS b,
CEIL(-4.1) AS c,
CEILING(7.0001) AS d;
Результат:
Разберём каждое значение.
CEIL(4.1) возвращает 5, потому что ближайшее целое сверху — это 5.
CEIL(4.0) возвращает 4, потому что число уже целое.
CEIL(-4.1) возвращает -4, потому что вверх на числовой оси — это в сторону большего числа. А -4 больше, чем -4.1.
CEILING(7.0001) возвращает 8, потому что даже маленькая дробная часть тянет число вверх.
CEIL и CEILING — одно и то же
В SQL можно встретить оба варианта:
SELECT CEIL(4.2) AS value;
И такой:
SELECT CEILING(4.2) AS value;
Оба запроса возвращают:
Обычно выбирают более короткий вариант CEIL, но CEILING иногда читается понятнее для новичков, потому что прямо напоминает английское слово «потолок».
FLOOR — это «пол», округление вниз.
CEILING — это «потолок», округление вверх.
CEIL всегда округляет вверх
Важно не путать CEIL с обычным округлением.
SELECT
CEIL(4.1) AS ceil_value,
ROUND(4.1) AS round_value;
Результат:
| ceil_value |
round_value |
| 5 |
4 |
ROUND(4.1) возвращает 4, потому что 4.1 ближе к 4.
А CEIL(4.1) возвращает 5, потому что ему всё равно, к чему число ближе. Он всегда идёт вверх.
Ещё пример:
SELECT
CEIL(4.9) AS ceil_value,
ROUND(4.9) AS round_value;
Результат:
| ceil_value |
round_value |
| 5 |
5 |
Здесь результаты совпали, но причина разная.
ROUND выбрал 5, потому что 4.9 ближе к 5.
CEIL выбрал 5, потому что это ближайшее целое сверху.
Отрицательные числа: вверх значит к нулю
С положительными числами всё выглядит очевидно: 4.1 округляется до 5.
Но с отрицательными значениями у новичков часто возникает путаница.
SELECT
CEIL(-4.1) AS value;
Результат:
Почему не -5?
Потому что CEIL идёт вверх по числовой оси, то есть к большему числу.
На числовой оси это выглядит так:
-6 -5 -4 -3 -2 -1 0
^
-4.1 goes up to -4
Число -4 больше, чем -4.1, поэтому это и есть округление вверх.
Простое правило:
для положительных чисел CEIL уходит дальше от нуля;
для отрицательных чисел CEIL идёт к нулю.
Подсчёт страниц пагинации
Самый классический пример для CEIL — пагинация.
Допустим, на странице показывается 25 заказов. Всего оплаченных заказов 126. Сколько нужно страниц?
Если просто разделить 126 на 25, получится 5.04. Но страниц не может быть 5.04. Нужна шестая страница, даже если на ней будет всего один заказ.
SELECT
COUNT(*) AS total_orders,
CEIL(COUNT(*)::numeric / 25) AS total_pages
FROM orders
WHERE status = 'paid';
Результат может быть таким:
| total_orders |
total_pages |
| 126 |
6 |
Вот в этом и сила CEIL: он превращает неполный остаток в ещё одну целую страницу.
Почему нельзя использовать ROUND для страниц
Представьте, что у нас 26 заказов и 25 заказов на страницу.
SELECT
CEIL(26::numeric / 25) AS pages_by_ceil,
ROUND(26::numeric / 25) AS pages_by_round;
Результат:
| pages_by_ceil |
pages_by_round |
| 2 |
1 |
26 / 25 — это 1.04.
ROUND округляет к ближайшему целому и получает 1.
Но это неверно для пагинации: один заказ не поместился на первую страницу, значит нужна вторая.
Для страниц, коробок, батчей и контейнеров почти всегда нужен именно CEIL, а не ROUND.
Пагинация по группам
CEIL можно использовать не только для всей таблицы, но и по группам.
Например, нужно понять, сколько страниц понадобится для пользователей из каждой страны, если показывать по 20 пользователей на страницу.
SELECT
country,
COUNT(*) AS users,
CEIL(COUNT(*)::numeric / 20) AS pages
FROM users
GROUP BY country
ORDER BY users DESC;
Результат может быть таким:
| country |
users |
pages |
| DE |
83 |
5 |
| ES |
41 |
3 |
| PL |
20 |
1 |
| FR |
7 |
1 |
Как читать:
- для
DE нужно 5 страниц, потому что 80 пользователей поместятся на 4 страницы, а ещё 3 уйдут на пятую;
- для
PL нужна 1 страница, потому что 20 пользователей помещаются ровно;
- для
FR тоже нужна 1 страница, хотя пользователей всего 7.
Главная ловушка: целочисленное деление
В PostgreSQL есть важный нюанс: если делить целое число на целое число, результат тоже будет целым.
SELECT 5 / 2 AS value;
Результат:
Не 2.5, а 2.
Дробная часть уже потерялась. Поэтому такой запрос даст неправильный результат:
SELECT CEIL(5 / 2) AS pages;
Результат:
А правильно должно быть 3.
Почему так произошло? Сначала SQL выполнил 5 / 2 и получил 2. Только потом применил CEIL. Но округлять уже нечего: дробная часть исчезла раньше.
Правильный вариант — привести хотя бы одно число к дробному типу:
SELECT CEIL(5::numeric / 2) AS pages;
Результат:
Можно и так:
SELECT CEIL(5.0 / 2) AS pages;
Главная мысль: если хотите, чтобы CEIL увидел дробную часть, не теряйте её на этапе деления.
Почему эта ошибка коварная
Ошибка с целочисленным делением особенно неприятна, потому что на ровных данных всё выглядит правильно.
SELECT
CEIL(100 / 25) AS wrong_but_looks_ok,
CEIL(100::numeric / 25) AS correct;
Результат:
| wrong_but_looks_ok |
correct |
| 4 |
4 |
Оба варианта дали 4. Кажется, что всё хорошо.
Но стоит появиться неполной последней странице:
SELECT
CEIL(101 / 25) AS wrong_result,
CEIL(101::numeric / 25) AS correct_result;
Результат:
| wrong_result |
correct_result |
| 4 |
5 |
Вот здесь баг становится заметным.
Поэтому в формулах пагинации лучше сразу писать явно:
CEIL(total::numeric / per_page)
или приводить к дробному типу другой операнд.
CEIL для коробок, батчей и контейнеров
Пагинация — только один пример. Та же логика работает в любых задачах, где нужно понять, сколько целых единиц понадобится.
Например, есть товары, и в одну коробку помещается 12 штук. Нужно посчитать, сколько коробок нужно для каждого заказа.
SELECT
order_id,
items_count,
CEIL(items_count::numeric / 12) AS boxes
FROM orders
ORDER BY order_id;
Результат:
| order_id |
items_count |
boxes |
| 101 |
12 |
1 |
| 102 |
13 |
2 |
| 103 |
24 |
2 |
| 104 |
25 |
3 |
Заказ на 13 товаров требует 2 коробки. Нельзя взять одну коробку и положить туда лишний товар «сбоку».
Вот где CEIL читается особенно естественно: любое превышение лимита требует ещё одну целую единицу.
Бакетинг: разбиение по верхней границе
CEIL удобно использовать для разбиения чисел по диапазонам, когда нужно получить верхнюю границу корзины.
Допустим, мы хотим сгруппировать сотрудников по зарплатным полкам шириной 10000.
SELECT
CEIL(salary / 10000.0) * 10000 AS salary_band,
COUNT(*) AS people
FROM employees
GROUP BY CEIL(salary / 10000.0) * 10000
ORDER BY salary_band;
Результат может быть таким:
| salary_band |
people |
| 40000 |
3 |
| 50000 |
8 |
| 60000 |
12 |
| 70000 |
5 |
Здесь salary_band — это верхняя граница диапазона.
Например:
| salary |
salary_band |
| 40000 |
40000 |
| 41000 |
50000 |
| 49999 |
50000 |
| 50000 |
50000 |
| 50100 |
60000 |
Зарплата 41000 попадает в полку 50000, потому что это диапазон до 50000.
Если вам нужна нижняя граница диапазона, лучше использовать FLOOR, а не CEIL.
CEIL для ценовых сегментов
Похожим способом можно разбить суммы заказов на ценовые сегменты.
SELECT
CEIL(amount / 100.0) AS price_bucket,
COUNT(*) AS orders
FROM orders
GROUP BY CEIL(amount / 100.0)
ORDER BY price_bucket;
Результат:
| price_bucket |
orders |
| 1 |
18 |
| 2 |
37 |
| 3 |
24 |
| 4 |
9 |
Здесь price_bucket = 1 означает суммы до 100, price_bucket = 2 — суммы выше 100 и до 200, и так далее.
Если хочется видеть не номер корзины, а верхнюю границу, можно умножить обратно на 100:
SELECT
CEIL(amount / 100.0) * 100 AS price_limit,
COUNT(*) AS orders
FROM orders
GROUP BY CEIL(amount / 100.0) * 100
ORDER BY price_limit;
Результат:
| price_limit |
orders |
| 100 |
18 |
| 200 |
37 |
| 300 |
24 |
| 400 |
9 |
Так отчёт становится понятнее: price_limit = 300 — это заказы, которые попали в сегмент до 300.
CEIL, FLOOR и ROUND: разница
У этих функций похожие названия, но разный смысл.
CEIL округляет вверх.
FLOOR округляет вниз.
ROUND округляет к ближайшему значению.
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);
Результат:
| x |
up |
down |
nearest |
| 2.1 |
3 |
2 |
2 |
| 2.5 |
3 |
2 |
3 |
| 2.9 |
3 |
2 |
3 |
| -2.5 |
-2 |
-3 |
-3 |
Как выбирать:
- нужна верхняя граница —
CEIL;
- нужна нижняя граница —
FLOOR;
- нужно обычное округление —
ROUND;
- нужно просто отбросить дробную часть —
TRUNC.
Почему для пагинации нужен именно CEIL
Ещё раз закрепим на бытовом примере.
У нас есть 51 пользователь. На одной странице помещается 50.
SELECT
CEIL(51::numeric / 50) AS pages_by_ceil,
FLOOR(51::numeric / 50) AS pages_by_floor,
ROUND(51::numeric / 50) AS pages_by_round;
Результат:
| pages_by_ceil |
pages_by_floor |
pages_by_round |
| 2 |
1 |
1 |
Правильный ответ — 2 страницы.
FLOOR ошибся, потому что отбросил неполную страницу.
ROUND тоже ошибся, потому что 1.02 ближе к 1, чем к 2.
CEIL дал верный результат, потому что даже один лишний пользователь требует ещё одну страницу.
Что будет с NULL
Если передать в CEIL значение NULL, результат тоже будет NULL.
SELECT CEIL(NULL) AS value;
Результат:
SQL не может округлить неизвестное значение. Поэтому неизвестность остаётся неизвестностью.
В отчётах это важно. Например, если цена может быть пустой:
SELECT
id,
CEIL(amount / 100.0) AS price_bucket
FROM orders;
Для строк, где amount равен NULL, price_bucket тоже будет NULL.
Если по бизнес-логике пустую сумму нужно считать нулём, это надо написать явно:
SELECT
id,
CEIL(COALESCE(amount, 0) / 100.0) AS price_bucket
FROM orders;
Но не стоит автоматически заменять все NULL на нули. Сначала решите, что значит пустое значение в вашей задаче.
Тип результата
В PostgreSQL результат CEIL зависит от типа аргумента.
Если передать numeric, результат останется numeric.
Если передать double precision, результат останется double precision.
Выглядеть значение может как целое, но тип не всегда будет integer.
Например:
SELECT CEIL(4.2::numeric) AS value;
Результат выглядит так:
Но это не обязательно обычный integer.
Если вам нужен именно целочисленный тип, добавьте явное приведение:
SELECT CEIL(4.2)::int AS value;
Это полезно, если результат дальше используется как номер страницы, количество коробок или номер батча.
Различия между СУБД
В PostgreSQL CEIL и CEILING работают одинаково. Главная практическая ловушка — целочисленное деление: 5 / 2 даёт 2, поэтому для точного результата нужно привести число к numeric или использовать дробный литерал.
SELECT CEIL(5::numeric / 2) AS pages;
В MySQL CEIL и CEILING тоже являются синонимами. Обычный оператор / выполняет деление с дробной частью, а для целочисленного деления используется DIV. Поэтому при переносе запросов между СУБД важно смотреть не только на CEIL, но и на сам оператор деления.
В ClickHouse функция называется ceil. У неё есть расширение со вторым аргументом — точностью округления.
Например, можно округлить вверх до одного знака после запятой:
SELECT ceil(4.123, 1) AS value;
Результат:
Это удобно, но такой синтаксис не является универсальным для всех СУБД.
Частые ошибки с CEIL
Первая ошибка — использовать ROUND там, где нужен CEIL.
SELECT ROUND(26::numeric / 25) AS pages;
Результат:
Для пагинации это неверно. Нужна вторая страница.
Вторая ошибка — забыть про целочисленное деление в PostgreSQL.
SELECT CEIL(26 / 25) AS pages;
Результат:
Правильно:
SELECT CEIL(26::numeric / 25) AS pages;
Результат:
Третья ошибка — не понимать, что происходит с отрицательными числами.
SELECT CEIL(-4.9) AS value;
Результат:
Это не ошибка SQL. Это округление вверх по числовой оси.
Четвёртая ошибка — не определить смысл корзины.
Если вы пишете:
SELECT CEIL(amount / 100.0) * 100 AS bucket
FROM orders;
то bucket обычно означает верхнюю границу диапазона.
Если вам нужна нижняя граница, формула должна быть через FLOOR.
Когда использовать CEIL
CEIL нужен в задачах, где любой остаток требует ещё одну целую единицу.
Например:
- посчитать количество страниц;
- определить число коробок для товаров;
- разбить данные на батчи;
- найти верхнюю границу ценового сегмента;
- разложить зарплаты по верхним полкам;
- округлить значение строго вверх;
- посчитать, сколько контейнеров нужно для заданного количества объектов.
Хороший мысленный тест:
Можно ли проигнорировать остаток?
Если нельзя, скорее всего, нужен CEIL.
Главное из статьи
CEIL округляет число вверх до ближайшего целого, которое не меньше исходного значения.
SELECT CEIL(4.1) AS value;
Результат:
CEIL и CEILING — синонимы.
SELECT CEILING(4.1) AS value;
Результат:
Для отрицательных чисел округление вверх идёт в сторону нуля:
SELECT CEIL(-4.1) AS value;
Результат:
Для пагинации CEIL подходит лучше, чем ROUND и FLOOR, потому что даже одна лишняя строка требует ещё одну страницу.
SELECT CEIL(26::numeric / 25) AS pages;
Результат:
В PostgreSQL важно не потерять дробную часть из-за целочисленного деления. Поэтому в формулах вроде пагинации приводите число к дробному типу:
SELECT CEIL(total_count::numeric / per_page) AS pages
FROM report_stats;
Главная мысль: CEIL нужен не для красивого округления, а для ситуаций, где маленький остаток превращается в ещё одну целую страницу, коробку, группу или верхнюю границу диапазона.
CEILокругляет число вверх до ближайшего целого.У функции есть второе имя —
CEILING. В большинстве популярных СУБД они означают одно и то же.Главная идея простая: если после деления остался хоть маленький хвостик, нужна ещё одна целая единица.
Например:
CEIL— это не про красивое округление. Это про инженерную логику: нельзя положить половину строки на страницу, половину заказа в батч или кусочек товара в несуществующую коробку.Что делает CEIL
CEIL(x)возвращает ближайшее целое число, которое не меньшеx.Проще говоря, функция округляет число вверх.
SELECT CEIL(4.1) AS a, CEIL(4.0) AS b, CEIL(-4.1) AS c, CEILING(7.0001) AS d;Результат:
Разберём каждое значение.
CEIL(4.1)возвращает5, потому что ближайшее целое сверху — это 5.CEIL(4.0)возвращает4, потому что число уже целое.CEIL(-4.1)возвращает-4, потому что вверх на числовой оси — это в сторону большего числа. А-4больше, чем-4.1.CEILING(7.0001)возвращает8, потому что даже маленькая дробная часть тянет число вверх.CEIL и CEILING — одно и то же
В SQL можно встретить оба варианта:
SELECT CEIL(4.2) AS value;И такой:
SELECT CEILING(4.2) AS value;Оба запроса возвращают:
Обычно выбирают более короткий вариант
CEIL, ноCEILINGиногда читается понятнее для новичков, потому что прямо напоминает английское слово «потолок».FLOOR— это «пол», округление вниз.CEILING— это «потолок», округление вверх.CEIL всегда округляет вверх
Важно не путать
CEILс обычным округлением.SELECT CEIL(4.1) AS ceil_value, ROUND(4.1) AS round_value;Результат:
ROUND(4.1)возвращает4, потому что 4.1 ближе к 4.А
CEIL(4.1)возвращает5, потому что ему всё равно, к чему число ближе. Он всегда идёт вверх.Ещё пример:
SELECT CEIL(4.9) AS ceil_value, ROUND(4.9) AS round_value;Результат:
Здесь результаты совпали, но причина разная.
ROUNDвыбрал 5, потому что 4.9 ближе к 5.CEILвыбрал 5, потому что это ближайшее целое сверху.Отрицательные числа: вверх значит к нулю
С положительными числами всё выглядит очевидно: 4.1 округляется до 5.
Но с отрицательными значениями у новичков часто возникает путаница.
SELECT CEIL(-4.1) AS value;Результат:
Почему не
-5?Потому что
CEILидёт вверх по числовой оси, то есть к большему числу.На числовой оси это выглядит так:
Число
-4больше, чем-4.1, поэтому это и есть округление вверх.Простое правило:
для положительных чисел
CEILуходит дальше от нуля;для отрицательных чисел
CEILидёт к нулю.Подсчёт страниц пагинации
Самый классический пример для
CEIL— пагинация.Допустим, на странице показывается 25 заказов. Всего оплаченных заказов 126. Сколько нужно страниц?
Если просто разделить 126 на 25, получится 5.04. Но страниц не может быть 5.04. Нужна шестая страница, даже если на ней будет всего один заказ.
SELECT COUNT(*) AS total_orders, CEIL(COUNT(*)::numeric / 25) AS total_pages FROM orders WHERE status = 'paid';Результат может быть таким:
Вот в этом и сила
CEIL: он превращает неполный остаток в ещё одну целую страницу.Почему нельзя использовать ROUND для страниц
Представьте, что у нас 26 заказов и 25 заказов на страницу.
SELECT CEIL(26::numeric / 25) AS pages_by_ceil, ROUND(26::numeric / 25) AS pages_by_round;Результат:
26 / 25— это1.04.ROUNDокругляет к ближайшему целому и получает1.Но это неверно для пагинации: один заказ не поместился на первую страницу, значит нужна вторая.
Для страниц, коробок, батчей и контейнеров почти всегда нужен именно
CEIL, а неROUND.Пагинация по группам
CEILможно использовать не только для всей таблицы, но и по группам.Например, нужно понять, сколько страниц понадобится для пользователей из каждой страны, если показывать по 20 пользователей на страницу.
SELECT country, COUNT(*) AS users, CEIL(COUNT(*)::numeric / 20) AS pages FROM users GROUP BY country ORDER BY users DESC;Результат может быть таким:
Как читать:
DEнужно 5 страниц, потому что 80 пользователей поместятся на 4 страницы, а ещё 3 уйдут на пятую;PLнужна 1 страница, потому что 20 пользователей помещаются ровно;FRтоже нужна 1 страница, хотя пользователей всего 7.Главная ловушка: целочисленное деление
В PostgreSQL есть важный нюанс: если делить целое число на целое число, результат тоже будет целым.
SELECT 5 / 2 AS value;Результат:
Не
2.5, а2.Дробная часть уже потерялась. Поэтому такой запрос даст неправильный результат:
SELECT CEIL(5 / 2) AS pages;Результат:
А правильно должно быть
3.Почему так произошло? Сначала SQL выполнил
5 / 2и получил2. Только потом применилCEIL. Но округлять уже нечего: дробная часть исчезла раньше.Правильный вариант — привести хотя бы одно число к дробному типу:
SELECT CEIL(5::numeric / 2) AS pages;Результат:
Можно и так:
SELECT CEIL(5.0 / 2) AS pages;Главная мысль: если хотите, чтобы
CEILувидел дробную часть, не теряйте её на этапе деления.Почему эта ошибка коварная
Ошибка с целочисленным делением особенно неприятна, потому что на ровных данных всё выглядит правильно.
SELECT CEIL(100 / 25) AS wrong_but_looks_ok, CEIL(100::numeric / 25) AS correct;Результат:
Оба варианта дали 4. Кажется, что всё хорошо.
Но стоит появиться неполной последней странице:
SELECT CEIL(101 / 25) AS wrong_result, CEIL(101::numeric / 25) AS correct_result;Результат:
Вот здесь баг становится заметным.
Поэтому в формулах пагинации лучше сразу писать явно:
CEIL(total::numeric / per_page)или приводить к дробному типу другой операнд.
CEIL для коробок, батчей и контейнеров
Пагинация — только один пример. Та же логика работает в любых задачах, где нужно понять, сколько целых единиц понадобится.
Например, есть товары, и в одну коробку помещается 12 штук. Нужно посчитать, сколько коробок нужно для каждого заказа.
SELECT order_id, items_count, CEIL(items_count::numeric / 12) AS boxes FROM orders ORDER BY order_id;Результат:
Заказ на 13 товаров требует 2 коробки. Нельзя взять одну коробку и положить туда лишний товар «сбоку».
Вот где
CEILчитается особенно естественно: любое превышение лимита требует ещё одну целую единицу.Бакетинг: разбиение по верхней границе
CEILудобно использовать для разбиения чисел по диапазонам, когда нужно получить верхнюю границу корзины.Допустим, мы хотим сгруппировать сотрудников по зарплатным полкам шириной 10000.
SELECT CEIL(salary / 10000.0) * 10000 AS salary_band, COUNT(*) AS people FROM employees GROUP BY CEIL(salary / 10000.0) * 10000 ORDER BY salary_band;Результат может быть таким:
Здесь
salary_band— это верхняя граница диапазона.Например:
Зарплата
41000попадает в полку50000, потому что это диапазон до 50000.Если вам нужна нижняя граница диапазона, лучше использовать
FLOOR, а неCEIL.CEIL для ценовых сегментов
Похожим способом можно разбить суммы заказов на ценовые сегменты.
SELECT CEIL(amount / 100.0) AS price_bucket, COUNT(*) AS orders FROM orders GROUP BY CEIL(amount / 100.0) ORDER BY price_bucket;Результат:
Здесь
price_bucket = 1означает суммы до 100,price_bucket = 2— суммы выше 100 и до 200, и так далее.Если хочется видеть не номер корзины, а верхнюю границу, можно умножить обратно на 100:
SELECT CEIL(amount / 100.0) * 100 AS price_limit, COUNT(*) AS orders FROM orders GROUP BY CEIL(amount / 100.0) * 100 ORDER BY price_limit;Результат:
Так отчёт становится понятнее:
price_limit = 300— это заказы, которые попали в сегмент до 300.CEIL, FLOOR и ROUND: разница
У этих функций похожие названия, но разный смысл.
CEILокругляет вверх.FLOORокругляет вниз.ROUNDокругляет к ближайшему значению.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;FLOOR;ROUND;TRUNC.Почему для пагинации нужен именно CEIL
Ещё раз закрепим на бытовом примере.
У нас есть 51 пользователь. На одной странице помещается 50.
SELECT CEIL(51::numeric / 50) AS pages_by_ceil, FLOOR(51::numeric / 50) AS pages_by_floor, ROUND(51::numeric / 50) AS pages_by_round;Результат:
Правильный ответ — 2 страницы.
FLOORошибся, потому что отбросил неполную страницу.ROUNDтоже ошибся, потому что1.02ближе к1, чем к2.CEILдал верный результат, потому что даже один лишний пользователь требует ещё одну страницу.Что будет с NULL
Если передать в
CEILзначениеNULL, результат тоже будетNULL.SELECT CEIL(NULL) AS value;Результат:
SQL не может округлить неизвестное значение. Поэтому неизвестность остаётся неизвестностью.
В отчётах это важно. Например, если цена может быть пустой:
SELECT id, CEIL(amount / 100.0) AS price_bucket FROM orders;Для строк, где
amountравенNULL,price_bucketтоже будетNULL.Если по бизнес-логике пустую сумму нужно считать нулём, это надо написать явно:
SELECT id, CEIL(COALESCE(amount, 0) / 100.0) AS price_bucket FROM orders;Но не стоит автоматически заменять все
NULLна нули. Сначала решите, что значит пустое значение в вашей задаче.Тип результата
В PostgreSQL результат
CEILзависит от типа аргумента.Если передать
numeric, результат останетсяnumeric.Если передать
double precision, результат останетсяdouble precision.Выглядеть значение может как целое, но тип не всегда будет
integer.Например:
SELECT CEIL(4.2::numeric) AS value;Результат выглядит так:
Но это не обязательно обычный
integer.Если вам нужен именно целочисленный тип, добавьте явное приведение:
SELECT CEIL(4.2)::int AS value;Это полезно, если результат дальше используется как номер страницы, количество коробок или номер батча.
Различия между СУБД
В PostgreSQL
CEILиCEILINGработают одинаково. Главная практическая ловушка — целочисленное деление:5 / 2даёт2, поэтому для точного результата нужно привести число кnumericили использовать дробный литерал.SELECT CEIL(5::numeric / 2) AS pages;В MySQL
CEILиCEILINGтоже являются синонимами. Обычный оператор/выполняет деление с дробной частью, а для целочисленного деления используетсяDIV. Поэтому при переносе запросов между СУБД важно смотреть не только наCEIL, но и на сам оператор деления.В ClickHouse функция называется
ceil. У неё есть расширение со вторым аргументом — точностью округления.Например, можно округлить вверх до одного знака после запятой:
SELECT ceil(4.123, 1) AS value;Результат:
Это удобно, но такой синтаксис не является универсальным для всех СУБД.
Частые ошибки с CEIL
Первая ошибка — использовать
ROUNDтам, где нуженCEIL.SELECT ROUND(26::numeric / 25) AS pages;Результат:
Для пагинации это неверно. Нужна вторая страница.
Вторая ошибка — забыть про целочисленное деление в PostgreSQL.
SELECT CEIL(26 / 25) AS pages;Результат:
Правильно:
SELECT CEIL(26::numeric / 25) AS pages;Результат:
Третья ошибка — не понимать, что происходит с отрицательными числами.
SELECT CEIL(-4.9) AS value;Результат:
Это не ошибка SQL. Это округление вверх по числовой оси.
Четвёртая ошибка — не определить смысл корзины.
Если вы пишете:
SELECT CEIL(amount / 100.0) * 100 AS bucket FROM orders;то
bucketобычно означает верхнюю границу диапазона.Если вам нужна нижняя граница, формула должна быть через
FLOOR.Когда использовать CEIL
CEILнужен в задачах, где любой остаток требует ещё одну целую единицу.Например:
Хороший мысленный тест:
Если нельзя, скорее всего, нужен
CEIL.Главное из статьи
CEILокругляет число вверх до ближайшего целого, которое не меньше исходного значения.SELECT CEIL(4.1) AS value;Результат:
CEILиCEILING— синонимы.SELECT CEILING(4.1) AS value;Результат:
Для отрицательных чисел округление вверх идёт в сторону нуля:
SELECT CEIL(-4.1) AS value;Результат:
Для пагинации
CEILподходит лучше, чемROUNDиFLOOR, потому что даже одна лишняя строка требует ещё одну страницу.SELECT CEIL(26::numeric / 25) AS pages;Результат:
В PostgreSQL важно не потерять дробную часть из-за целочисленного деления. Поэтому в формулах вроде пагинации приводите число к дробному типу:
SELECT CEIL(total_count::numeric / per_page) AS pages FROM report_stats;Главная мысль:
CEILнужен не для красивого округления, а для ситуаций, где маленький остаток превращается в ещё одну целую страницу, коробку, группу или верхнюю границу диапазона.