sqlpostgresqltruncfloor

TRUNC в SQL: как отбросить дробную часть без округления

TRUNC срезает дробную часть, не глядя на следующие цифры; разберём второй аргумент, деньги и отличие от FLOOR на возвратах.

9 мин чтенияСправочникsql · postgresql · trunc · floor · rounding · mysql

TRUNC в SQL — это функция, которая отрезает лишние цифры у числа без округления.

Она не думает: «Ближе к 3 или к 4?»
Она просто берёт число и срезает всё, что не должно остаться.

Например:

Число TRUNC
3.99 3
3.01 3
2.50 2
-3.99 -3

Главная идея простая:

TRUNC не округляет число. Он отбрасывает лишнюю часть.

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

Чем TRUNC отличается от ROUND

Начнём с самого понятного сравнения.

ROUND округляет число по правилам округления.
TRUNC просто срезает лишнее.

SELECT
  round(3.99) AS rounded_a,
  trunc(3.99) AS truncated_a,
  round(2.50) AS rounded_b,
  trunc(2.50) AS truncated_b;

Результат будет примерно такой:

rounded_a truncated_a rounded_b truncated_b
4 3 3 2

ROUND(3.99) превращает число в 4, потому что 3.99 ближе к 4.

А TRUNC(3.99) возвращает 3, потому что всё после точки просто отбрасывается.

Можно представить так:

  • ROUND — «округли как правильно по математике»;
  • TRUNC — «срежь лишнее и ничего не улучшай».

Базовый синтаксис

В PostgreSQL простая форма выглядит так:

SELECT trunc(123.4567) AS result;

Результат:

result
123

Функция убрала дробную часть .4567 и оставила только целую часть.

Ещё несколько примеров:

SELECT
  trunc(3.99) AS a,
  trunc(3.01) AS b,
  trunc(-3.99) AS c,
  trunc(2.50) AS d;

Результат:

a b c d
3 3 -3 2

Обратите внимание на отрицательное число: trunc(-3.99) вернул -3, а не -4.

Почему так? Потому что TRUNC отбрасывает дробную часть в сторону нуля.

TRUNC двигается к нулю

Это одна из самых важных деталей.

Для положительных чисел всё интуитивно:

Число Результат
3.99 3
3.10 3
3.00 3

А вот с отрицательными числами новички часто ошибаются.

SELECT
  trunc(-3.99) AS a,
  trunc(-3.10) AS b,
  trunc(-3.00) AS c;

Результат:

a b c
-3 -3 -3

TRUNC не делает число «ещё меньше». Он просто срезает дробную часть.

То есть -3.99 превращается в -3, потому что -3 ближе к нулю, чем -4.

TRUNC с количеством знаков после запятой

У TRUNC может быть второй аргумент. Он показывает, сколько знаков после запятой нужно оставить.

SELECT
  trunc(123.4567, 2) AS two_digits,
  trunc(123.4567, 1) AS one_digit,
  trunc(123.4567, 0) AS zero_digits;

Результат:

two_digits one_digit zero_digits
123.45 123.4 123

Смысл такой:

  • trunc(123.4567, 2) оставляет два знака после запятой;
  • trunc(123.4567, 1) оставляет один знак;
  • trunc(123.4567, 0) оставляет только целую часть.

И снова никакого округления.

123.4567 при усечении до двух знаков становится 123.45, а не 123.46.

Отрицательный второй аргумент

Второй аргумент может быть отрицательным.

Тогда TRUNC начинает срезать разряды уже слева от запятой: единицы, десятки, сотни.

SELECT
  trunc(123.4567, -1) AS tens,
  trunc(123.4567, -2) AS hundreds,
  trunc(9876.543, -3) AS thousands;

Результат:

tens hundreds thousands
120 100 9000

Как это читать:

  • -1 срезает до десятков;
  • -2 срезает до сотен;
  • -3 срезает до тысяч.

Это удобно, когда нужно разложить значения по грубым корзинам.

Например, зарплату 4999 можно отнести к корзине 4900, а не округлить до 5000.

SELECT
  name,
  salary,
  trunc(salary, -2) AS salary_bucket
FROM employees
ORDER BY salary DESC;

Если у сотрудника зарплата 4999, то salary_bucket будет 4900.

TRUNC не дотягивает число вверх. Он режет.

Пример: корзины для сумм заказов

Допустим, есть таблица заказов:

CREATE TABLE orders (
  id bigint PRIMARY KEY,
  user_id bigint NOT NULL,
  amount numeric NOT NULL,
  status text NOT NULL
);

Хотим сгруппировать оплаченные заказы по сотням: 100, 200, 300, 400 и так далее.

SELECT
  trunc(amount, -2) AS amount_bucket,
  count(*) AS orders_count
FROM orders
WHERE status = 'paid'
GROUP BY trunc(amount, -2)
ORDER BY amount_bucket;

Если суммы такие:

amount
120
180
249
310
399

То корзины будут такими:

amount amount_bucket
120 100
180 100
249 200
310 300
399 300

Это не округление до ближайшей сотни. Это именно усечение.

399 попадёт в 300, а не в 400.

TRUNC против FLOOR

Для положительных чисел TRUNC и FLOOR часто дают одинаковый результат.

SELECT
  floor(3.8) AS floor_value,
  trunc(3.8) AS trunc_value;

Результат:

floor_value trunc_value
3 3

Но на отрицательных числах начинается важное отличие.

FLOOR округляет вниз, то есть в сторону минус бесконечности.
TRUNC отбрасывает дробную часть в сторону нуля.

SELECT
  floor(-3.2) AS floor_negative,
  trunc(-3.2) AS trunc_negative,
  floor(3.2) AS floor_positive,
  trunc(3.2) AS trunc_positive;

Результат:

floor_negative trunc_negative floor_positive trunc_positive
-4 -3 3 3

Вот здесь легко поймать неприятный баг.

Если у вас есть возвраты, списания или корректировки с отрицательными суммами, FLOOR и TRUNC дадут разные значения.

SELECT
  id,
  amount,
  floor(amount) AS whole_floor,
  trunc(amount) AS whole_trunc
FROM orders
WHERE amount < 0;

Для суммы -15.30 результат будет таким:

amount whole_floor whole_trunc
-15.30 -16 -15

Если по бизнес-логике нужно именно отбросить дробную часть, берите TRUNC.

Если нужно округлить вниз по числовой оси, берите FLOOR.

Это разные операции.

TRUNC против CEIL

Есть ещё функция CEIL или CEILING. Она округляет число вверх.

SELECT
  trunc(4.1) AS truncated_value,
  floor(4.1) AS floor_value,
  ceil(4.1) AS ceil_value;

Результат:

truncated_value floor_value ceil_value
4 4 5

Для положительного числа 4.1:

  • TRUNC даёт 4;
  • FLOOR даёт 4;
  • CEIL даёт 5.

А для отрицательного числа:

SELECT
  trunc(-4.1) AS truncated_value,
  floor(-4.1) AS floor_value,
  ceil(-4.1) AS ceil_value;

Результат:

truncated_value floor_value ceil_value
-4 -5 -4

Поэтому при работе с отрицательными числами всегда проверяйте, что именно вы хотите: срезать дробь, округлить вниз или округлить вверх.

Где TRUNC полезен на практике

TRUNC нужен там, где округление может изменить смысл числа.

Например, у пользователя есть лимит 1000. После расчёта получилось 999.99.

Если использовать ROUND, можно случайно получить 1000.

Если использовать TRUNC, значение останется 999.

Такое поведение может быть важно в правилах:

  • комиссий;
  • процентов;
  • лимитов;
  • бонусов;
  • технических бакетов;
  • расчёта тарифа;
  • финансовых ограничений;
  • промежуточных аналитических метрик.

Главная мысль:

TRUNC используют, когда число не должно увеличиться из-за округления.

Пример: комиссия без округления вверх

Допустим, платформа берёт комиссию 2.9% с оплаченных заказов. По договору комиссия должна считаться с точностью до копеек, но без округления вверх.

SELECT
  u.id,
  sum(o.amount) AS gross_amount,
  round(sum(o.amount) * 0.029, 2) AS fee_rounded,
  trunc(sum(o.amount) * 0.029, 2) AS fee_truncated
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE o.status = 'paid'
GROUP BY u.id;

Разница может быть небольшой на одной операции, но на тысячах операций она накапливается.

Например:

value ROUND(value, 2) TRUNC(value, 2)
2.575 2.58 2.57
10.999 11.00 10.99
99.995 100.00 99.99

ROUND может поднять число.
TRUNC никогда не поднимает его из-за отброшенных цифр.

Именно поэтому правило округления или усечения должно быть прописано в требованиях. Для денег это не вкусовщина, а часть бизнес-логики.

Важное правило для денег

С деньгами лучше работать через точные числовые типы, например numeric.

Не стоит без необходимости считать финансовые значения через float или double precision.

Причина в том, что числа с плавающей точкой хранятся в двоичном виде, и некоторые десятичные дроби не могут быть представлены идеально точно.

Например, значение вроде 0.29 внутри может храниться как число чуть меньше или чуть больше. Для технических расчётов это нормально, но для денег может дать неприятные сюрпризы.

Хороший вариант:

SELECT
  trunc(amount::numeric * 0.029, 2) AS fee
FROM orders
WHERE status = 'paid';

Если колонка amount уже имеет тип numeric, дополнительное приведение может быть не нужно. Но сама идея остаётся: для денег лучше выбирать точную арифметику.

Усечение не всегда правильно

Важно не превращать TRUNC в универсальную замену ROUND.

Во многих бухгалтерских и финансовых задачах как раз требуется обычное округление.

Например, если в требованиях сказано:

«Округлять до двух знаков после запятой по стандартным правилам».

Тогда нужен ROUND, а не TRUNC.

SELECT
  round(amount, 2) AS amount_rounded
FROM orders;

Если сказано:

«Отбрасывать лишние знаки без округления».

Тогда нужен TRUNC.

SELECT
  trunc(amount, 2) AS amount_truncated
FROM orders;

Не угадывайте. Для денег, комиссий и лимитов правило нужно уточнять и фиксировать явно.

Пример: сравнение ROUND и TRUNC в отчёте

Допустим, мы хотим показать сумму заказов пользователя и комиссию.

SELECT
  user_id,
  sum(amount) AS gross_amount,
  sum(amount) * 0.029 AS raw_fee,
  round(sum(amount) * 0.029, 2) AS rounded_fee,
  trunc(sum(amount) * 0.029, 2) AS truncated_fee
FROM orders
WHERE status = 'paid'
GROUP BY user_id
ORDER BY user_id;

Такой отчёт полезен для проверки: можно сразу увидеть исходную комиссию, округлённую комиссию и усечённую комиссию.

Если бизнес требует усечение, итоговую колонку берём из truncated_fee.

Если требует округление, берём из rounded_fee.

TRUNC и тип результата

В PostgreSQL поведение зависит от типа входного значения.

Для numeric результат будет numeric.

SELECT
  trunc(123.456::numeric, 2) AS result;

Для double precision есть форма trunc(x), которая возвращает значение с плавающей точкой.

На практике для начинающего важнее запомнить не типы, а смысл:

Если нужна финансовая точность, работайте с numeric. Если это технические измерения вроде latency или координат, иногда допустимы вещественные типы.

TRUNC в MySQL

В MySQL функция называется не TRUNC, а TRUNCATE.

И второй аргумент там обязателен.

SELECT
  truncate(123.4567, 2) AS two_digits,
  truncate(123.4567, 0) AS zero_digits;

Результат:

two_digits zero_digits
123.45 123

В MySQL можно использовать и отрицательный второй аргумент:

SELECT
  truncate(123.4567, -1) AS tens,
  truncate(123.4567, -2) AS hundreds;

Результат:

tens hundreds
120 100

Важно не путать функцию TRUNCATE с командой TRUNCATE TABLE.

TRUNCATE TABLE orders;

Это уже совсем другая операция. Она очищает таблицу, а не работает с числом.

Название похоже, смысл совершенно другой.

TRUNC в ClickHouse

В ClickHouse можно встретить функции trunc и truncate.

Примерно смысл тот же: отбросить дробную часть или лишние знаки без обычного округления.

SELECT
  trunc(123.4567) AS integer_part,
  truncate(123.4567, 2) AS two_digits;

При переносе запроса между PostgreSQL, MySQL и ClickHouse проверяйте название функции и правила аргументов. Идея одна и та же, но синтаксис может отличаться.

Частая ошибка: использовать TRUNC там, где нужен ROUND

Допустим, в отчёте нужно показать средний чек до двух знаков после запятой.

SELECT
  trunc(avg(amount), 2) AS avg_amount
FROM orders
WHERE status = 'paid';

Технически запрос работает. Но вопрос: правильно ли это по смыслу?

Если отчёт должен показывать обычное округление, нужно писать так:

SELECT
  round(avg(amount), 2) AS avg_amount
FROM orders
WHERE status = 'paid';

TRUNC систематически срезает хвост и не даёт числу подняться. Поэтому он может немного занижать результат.

Для одной строки разница почти незаметна. Для большого отчёта, финансовой выгрузки или расчёта комиссии эта разница может стать существенной.

Частая ошибка: думать, что TRUNC и FLOOR одинаковые

Для положительных чисел они часто совпадают, поэтому возникает ложное ощущение, что это одно и то же.

Но на отрицательных значениях они расходятся:

SELECT
  amount,
  trunc(amount) AS truncated_value,
  floor(amount) AS floor_value
FROM transactions
WHERE amount < 0;

Если в таблице есть возвраты, штрафы, корректировки или отрицательные балансы, эта разница может повлиять на итоговые цифры.

Запоминаем:

  • TRUNC режет к нулю;
  • FLOOR округляет вниз;
  • CEIL округляет вверх;
  • ROUND округляет по правилам округления.

Когда выбирать TRUNC

Используйте TRUNC, когда правило звучит так:

«Оставить нужное количество знаков и отбросить всё остальное».

Примеры:

  • комиссия не должна округляться вверх;
  • лимит нельзя случайно увеличить;
  • значение нужно отнести к нижней корзине;
  • дробная часть технически не нужна;
  • число должно быть симметрично усечено для плюса и минуса;
  • в требованиях прямо написано «без округления».

Если же правило звучит как:

«Округлить до ближайшего значения».

Тогда используйте ROUND.

Главное

TRUNC в SQL отбрасывает дробную часть числа без округления.

trunc(3.99) вернёт 3, а не 4.
trunc(-3.99) вернёт -3, потому что усечение идёт к нулю.

С помощью второго аргумента можно оставить нужное количество знаков после запятой:

SELECT trunc(123.4567, 2) AS result;

Результат будет 123.45, а не 123.46.

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

TRUNC особенно полезен в расчётах комиссий, лимитов и технических корзин, где число не должно увеличиваться из-за округления.

Не путайте TRUNC с ROUND, FLOOR и CEIL: это разные функции с разным смыслом, особенно на отрицательных числах.

В PostgreSQL используется TRUNC, в MySQL — TRUNCATE, а команда TRUNCATE TABLE вообще не про числа, а про очистку таблицы.

Главное правило: если нужно именно срезать лишние цифры, используйте TRUNC. Если нужно округлить по правилам математики, используйте ROUND.

Закрепи на практике

Решай задачи в SQL-тренажёре с мгновенной проверкой и подсказками.

Открыть тренажёр