sqlpostgresqltruncfloor

SQL TRUNC: Dropping the Fractional Part Toward Zero Without Rounding

How TRUNC chops the fractional part toward zero, how TRUNC(x, n) cuts to a number of decimals, and why it differs from FLOOR on negatives.

3 min lukuaikaReferencesql · postgresql · trunc · floor · rounding · mysql
Tämä artikkeli on tällä hetkellä venäjäksi — englanninkielinen käännös on työn alla.

TRUNC отбрасывает дробную часть числа, не округляя его: всё, что после точки, просто срезается. В отличие от ROUND, направление здесь всегда одно — к нулю, поэтому результат предсказуем и для положительных, и для отрицательных значений.

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

Что делает TRUNC

В простейшей форме TRUNC(x) возвращает целую часть числа, отбрасывая дробную. Никакого округления к ближайшему: 3.99 превращается в 3, а не в 4.

SELECT
  TRUNC(3.99)   AS a,   -- 3
  TRUNC(3.01)   AS b,   -- 3
  TRUNC(-3.99)  AS c,   -- -3
  TRUNC(2.5)    AS d;   -- 2

Ключевые свойства:

  • Усечение всегда идёт к нулю, а не вниз: -3.99 даёт -3, потому что -3 ближе к нулю, чем -4.
  • ROUND(2.5) дало бы 3, а TRUNC(2.5)2: TRUNC вообще не смотрит на отброшенные цифры.
  • Тип результата — numeric, так что точность сохраняется.

TRUNC(x, n): усечение до знаков

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

SELECT
  TRUNC(123.4567, 2)  AS two_dp,    -- 123.45
  TRUNC(123.4567, 0)  AS zero_dp,   -- 123
  TRUNC(123.4567, -1) AS neg_one;   -- 120

Отрицательное n усекает слева от запятой: -1 обнуляет единицы, -2 — десятки. Применим это к таблице employees, чтобы показать зарплату «грубыми» сотнями без округления вверх:

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

Сотрудник с зарплатой 4999 попадёт в корзину 4900, а не 5000 — усечение никогда не «дотягивает» значение до следующего разряда.

TRUNC против FLOOR на отрицательных числах

Для положительных чисел TRUNC и FLOOR выглядят одинаково. Расхождение начинается на отрицательных: FLOOR округляет вниз (к минус бесконечности), а TRUNCк нулю.

SELECT
  FLOOR(-3.2) AS floor_neg,  -- -4
  TRUNC(-3.2) AS trunc_neg,  -- -3
  FLOOR(3.2)  AS floor_pos,  --  3
  TRUNC(3.2)  AS trunc_pos;  --  3

Это легко превратить в баг. Допустим, в orders хранятся возвраты с отрицательным amount, и вы считаете «целое число долларов»:

SELECT
  id,
  amount,
  FLOOR(amount) AS whole_floor,  -- -16 for -15.30
  TRUNC(amount) AS whole_trunc   -- -15 for -15.30
FROM orders
WHERE amount < 0;

Грабли: если нужна именно симметрия «отбросить дробь», берите TRUNC. FLOOR на возвратах завысит сумму списания на целый доллар.

Усечение денег vs округление

Деньги — отдельная больная тема. Бухгалтерия обычно требует округления (ROUND), но в некоторых расчётах — комиссии, начисление процентов, дробление цены — закон или договор предписывает именно усечение, чтобы не «дарить» клиенту лишний цент.

SELECT
  u.id,
  SUM(o.amount)                         AS gross,
  ROUND(SUM(o.amount) * 0.029, 2)       AS fee_rounded,   -- rounding
  TRUNC(SUM(o.amount) * 0.029, 2)       AS fee_truncated  -- truncation
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE o.status = 'paid'
GROUP BY u.id;

Помните:

  • ROUND(2.575, 2) может дать 2.58, а TRUNC(2.575, 2) — всегда 2.57, потому что отброшенная цифра вообще не учитывается.
  • Усечение систематически занижает результат, а округление — нет; на больших объёмах это копится в заметную разницу, поэтому выбирайте осознанно и фиксируйте правило в спецификации расчёта.
  • Считайте на numeric, а не на float: TRUNC от двоичного float может удивить из-за погрешности представления, когда значение вроде 0.29 хранится как чуть меньшее число.

MySQL: функция называется TRUNCATE

В MySQL нет функции TRUNC — её роль играет TRUNCATE, и второй аргумент там обязателен.

-- MySQL
SELECT
  TRUNCATE(123.4567, 2) AS two_dp,   -- 123.45
  TRUNCATE(123.4567, 0) AS zero_dp;  -- 123

Не путайте с командой TRUNCATE TABLE, которая мгновенно очищает таблицу целиком и не имеет ничего общего с округлением чисел — это совсем другая операция, относящаяся к DDL. В ClickHouse есть и trunc(x), и truncate(x, n) как синонимы, причём второй аргумент необязателен, как в PostgreSQL. Запомните одно: TRUNC — это «срезать к нулю без округления», а конкретное имя функции и обязательность второго аргумента зависят от вашей СУБД, так что при переносе запроса между движками этот фрагмент стоит перепроверять первым.

Короткий вывод: используйте TRUNC, когда правило расчёта требует именно усечения, а не «разумного» округления. Если бизнес-логика не фиксирует этот выбор явно, лучше зафиксировать его в требованиях до того, как цифры попадут в отчёт.

Harjoittele oikeilla tehtävillä

Ratkaise tehtäviä SQL-harjoittelussa välittömällä arvioinnilla ja vihjeillä.

Avaa harjoittelu