ROUND кажется одной из самых простых функций в SQL: взяли дробное число — получили ближайшее целое.
Например:
SELECT ROUND(3.14159) AS result;
Результат:
Но за этой простотой прячется важная деталь: округление бывает разным. Особенно на числах вроде 2.5, 3.5, -2.5, где значение стоит ровно посередине между двумя целыми.
Для обычного отчёта это может быть мелочью. А вот для денег, комиссий, зарплат, лимитов и сверок с внешними системами одна «лишняя» копейка уже становится настоящей проблемой. Причём проблема неприятная: она появляется редко, выглядит случайной и долго не находится.
Разберём, как работает ROUND в PostgreSQL, чем отличается numeric от double precision, почему деньги лучше не хранить в плавающей точке и почему нельзя переносить округление между PostgreSQL, MySQL и ClickHouse вслепую.
Что делает ROUND
ROUND округляет число до ближайшего целого.
Базовый синтаксис:
ROUND(number)
Пример:
SELECT
ROUND(3.14159) AS a,
ROUND(9.8) AS b,
ROUND(10.2) AS c;
Результат:
То есть функция смотрит на дробную часть и выбирает ближайшее целое число.
Если дробная часть меньше половины, число округляется вниз:
SELECT ROUND(4.4) AS result;
Результат:
Если дробная часть больше половины, число округляется вверх:
SELECT ROUND(4.6) AS result;
Результат:
А вот с ровной половиной начинается самое интересное.
Как ROUND округляет половинки в PostgreSQL
В PostgreSQL литерал вроде 2.5 обычно воспринимается как значение точного типа numeric.
Для numeric функция ROUND округляет половинки от нуля.
Это значит:
2.5 превращается в 3;
3.5 превращается в 4;
-2.5 превращается в -3;
-3.5 превращается в -4.
Пример:
SELECT
ROUND(3.14159) AS a,
ROUND(2.5) AS b,
ROUND(3.5) AS c,
ROUND(-2.5) AS d,
ROUND(-3.5) AS e;
Результат:
Фраза «от нуля» звучит немного непривычно, но смысл простой.
Положительное число при половинке уходит дальше вправо:
2.5 -> 3
Отрицательное число при половинке уходит дальше влево:
-2.5 -> -3
То есть число становится дальше от нуля.
ROUND для numeric и double precision
Главная ловушка в PostgreSQL: результат округления может зависеть от типа данных.
Для точного типа numeric половинки округляются от нуля.
А вот для double precision поведение может отличаться. Часто используется банковское округление: половинка уходит к ближайшему чётному числу.
Сравним:
SELECT
ROUND(2.5::numeric) AS numeric_25,
ROUND(3.5::numeric) AS numeric_35,
ROUND(2.5::double precision) AS double_25,
ROUND(3.5::double precision) AS double_35;
Результат на типичной системе может быть таким:
| numeric_25 |
numeric_35 |
double_25 |
double_35 |
| 3 |
4 |
2 |
4 |
Почему 2.5::double precision стало 2, а не 3?
Потому что при банковском округлении ровная половинка округляется к ближайшему чётному числу.
Для 2.5 соседи — 2 и 3. Чётное число — 2, поэтому результат 2.
Для 3.5 соседи — 3 и 4. Чётное число — 4, поэтому результат 4.
Это не ошибка PostgreSQL. Это другая стратегия округления.
Зачем вообще нужно банковское округление
Обычное округление половинок от нуля кажется более привычным: 2.5 стало 3, и всё понятно.
Но если округлять огромное количество чисел, постоянное округление половинок в одну сторону может давать систематическое смещение.
Банковское округление пытается это смещение уменьшить: иногда половинка округляется вниз, иногда вверх — в зависимости от ближайшего чётного числа.
Например:
2.5 -> 2
3.5 -> 4
4.5 -> 4
5.5 -> 6
На длинной дистанции такие округления меньше «тащат» сумму в одну сторону.
Но для разработчика важнее другое: нельзя просто сказать «я использую ROUND» и считать вопрос закрытым. Нужно понимать, какой тип данных вы округляете и какую стратегию использует ваша система.
Почему одно и то же число может округлиться по-разному
Посмотрите на этот запрос:
SELECT
ROUND(2.5) AS a,
ROUND(2.5::numeric) AS b,
ROUND(2.5::double precision) AS c;
Выглядит так, будто везде одно и то же число 2.5.
Но для базы это не совсем одно и то же. В первом и втором случае PostgreSQL работает с точным числом, а в третьем — с приблизительным числом с плавающей точкой.
Именно поэтому результат может отличаться.
Особенно опасно, когда в запросе смешиваются разные типы:
SELECT
ROUND(amount) AS rounded_amount
FROM orders;
Если amount имеет тип numeric, вы получите одну стратегию. Если amount имеет тип double precision, возможны другие нюансы.
Для финансовых расчётов лучше не оставлять это на волю случая. Приводите данные к точному типу явно:
SELECT
ROUND(amount::numeric) AS rounded_amount
FROM orders;
Но ещё лучше — изначально хранить деньги в numeric, а не в double precision.
Почему double precision опасен для денег
Тип double precision хранит числа приблизительно. Он быстрый и полезный для научных расчётов, измерений, координат, статистики, но для денег подходит плохо.
Причина в том, что многие десятичные дроби нельзя точно представить в двоичной плавающей точке.
Для человека число выглядит как 19.99.
А внутри оно может быть чем-то вроде:
19.989999999999998
Или наоборот:
19.990000000000002
Чаще всего это незаметно. Но при суммировании, расчётах комиссий и последующем округлении такие хвосты могут вылезти наружу.
Например:
SELECT
SUM(amount) AS raw_total,
ROUND(SUM(amount)) AS rounded_total
FROM orders
WHERE status = 'paid';
Если amount — это numeric(12,2), сумма считается точно.
Если amount — это double precision, сумма может накопить маленькую погрешность. И ROUND будет округлять уже не красивое человеческое число, а число с техническим хвостом.
Для денег правило простое: храните суммы в numeric.
Например:
CREATE TABLE payments (
id integer,
amount numeric(12, 2)
);
Так вы заранее убираете целый класс странных проблем.
Пример: округлить среднюю зарплату
Допустим, есть таблица сотрудников:
CREATE TABLE employees (
id integer,
dept text,
salary numeric(12, 2)
);
Нужно посчитать среднюю зарплату по отделам и округлить до целого.
SELECT
dept,
ROUND(AVG(salary)) AS avg_salary
FROM employees
GROUP BY dept
ORDER BY avg_salary DESC;
Результат может быть таким:
| dept |
avg_salary |
| Engineering |
185000 |
| Sales |
142000 |
| Support |
98000 |
Если salary хранится как numeric, то AVG(salary) тоже будет точным числовым результатом, и ROUND отработает предсказуемо.
Это хороший пример нормального применения ROUND: мы не меняем исходные данные, а готовим удобное значение для отчёта.
ROUND — не всегда просто косметика
Иногда разработчики воспринимают округление как украшение результата: мол, просто убрали лишние знаки после запятой.
Но в бизнес-логике округление может быть частью расчёта.
Например:
- округление комиссии;
- округление суммы к оплате;
- округление лимита;
- округление бонусов;
- округление налоговой базы;
- округление зарплатного отчёта.
Если база округляет одним способом, а приложение потом другим, могут появиться расхождения.
Например, база посчитала:
2.5 -> 3
А приложение округлило похожее значение по банковскому правилу:
2.5 -> 2
На одной строке это выглядит мелко. Но в отчёте на тысячи операций такие различия превращаются в вопросы от бухгалтерии, финансового отдела или внешней системы.
Поэтому для важных расчётов стоит заранее договориться:
- где именно происходит округление: в базе или в приложении;
- какой тип данных используется;
- какая стратегия округления считается правильной;
- можно ли округлять промежуточные значения или только финальный результат.
Не округляйте слишком рано
Ещё одна частая ошибка — округлять каждую строку отдельно, а потом суммировать.
Например:
SELECT
SUM(ROUND(amount)) AS total
FROM orders;
Это не то же самое, что сначала суммировать, а потом округлить:
SELECT
ROUND(SUM(amount)) AS total
FROM orders;
Разница может быть заметной.
Представим три суммы:
10.4
10.4
10.4
Если округлить каждую отдельно:
10 + 10 + 10 = 30
Если сначала сложить:
10.4 + 10.4 + 10.4 = 31.2
А потом округлить:
31
Получились разные итоги.
Поэтому перед округлением задайте себе вопрос: нужно округлять каждую операцию отдельно или только общий результат?
В финансовых и аналитических отчётах это принципиально.
ROUND с количеством знаков после запятой
У ROUND есть вариант со вторым аргументом:
ROUND(number, digits)
Он округляет не до целого, а до нужного количества знаков после запятой.
Например:
SELECT
ROUND(3.14159, 2) AS pi_2,
ROUND(9.876, 1) AS value_1;
Результат:
Это удобно для процентов, средних значений и денежных отчётов, где нужно показать две цифры после запятой.
Например:
SELECT
dept,
ROUND(AVG(salary), 2) AS avg_salary
FROM employees
GROUP BY dept;
Но логика остаётся той же: важно понимать тип данных и стратегию округления.
ROUND и отображение результата
Иногда округление используют только для красивого вывода:
SELECT
ROUND(conversion_rate * 100, 2) AS conversion_percent
FROM daily_metrics;
Это нормальный сценарий для отчёта.
Но не стоит путать округление для отображения и округление для хранения.
Если вы округлили значение в SELECT, исходные данные в таблице не изменились.
SELECT ROUND(amount, 2) AS amount_for_report
FROM payments;
Такой запрос просто показывает значение в удобном виде.
А вот если вы используете ROUND в UPDATE, вы уже меняете данные:
UPDATE payments
SET amount = ROUND(amount, 2);
С такими запросами нужно быть осторожнее: после обновления вы можете потерять исходную точность.
MySQL: ROUND и типы данных
В MySQL функция тоже называется ROUND.
Базовый пример:
SELECT ROUND(2.5) AS result;
Для точных типов вроде DECIMAL половинки обычно округляются от нуля.
Например:
SELECT ROUND(CAST(2.5 AS DECIMAL(10, 1))) AS result;
Результат:
А вот для приблизительных типов вроде DOUBLE поведение может зависеть от реализации и платформы. На практике можно встретить банковское округление.
Например:
SELECT
ROUND(CAST(2.5 AS DECIMAL(10, 1))) AS decimal_result,
ROUND(2.5e0) AS double_result;
На одной системе результат может выглядеть так:
| decimal_result |
double_result |
| 3 |
2 |
Главный вывод такой же, как в PostgreSQL: для денег и точных бизнес-расчётов используйте точные типы, а не DOUBLE.
ClickHouse: ROUND и банковское округление
В ClickHouse тоже есть функция round.
Но при переносе логики из PostgreSQL нужно быть особенно внимательным: стратегия округления половинок может отличаться от привычной.
Например, банковское округление даёт такие результаты:
SELECT
round(2.5) AS a,
round(3.5) AS b;
Результат может быть таким:
То есть 2.5 округляется к 2, потому что 2 — ближайшее чётное. А 3.5 округляется к 4, потому что 4 — ближайшее чётное.
В ClickHouse также есть функция roundBankers, которая явно подчёркивает банковскую стратегию:
SELECT
roundBankers(2.5) AS a,
roundBankers(3.5) AS b;
Результат:
Практическое правило: если переносите расчёты между PostgreSQL, MySQL и ClickHouse, обязательно проверьте контрольные значения:
2.5
3.5
-2.5
-3.5
И отдельно проверьте типы: точные числа и числа с плавающей точкой могут вести себя по-разному.
Как безопасно использовать ROUND в отчётах
Для обычных отчётов можно держаться такого шаблона.
Если считаете деньги:
SELECT
ROUND(SUM(amount), 2) AS total_amount
FROM payments
WHERE status = 'paid';
При этом amount лучше хранить как numeric.
Если считаете средние значения:
SELECT
dept,
ROUND(AVG(salary)) AS avg_salary
FROM employees
GROUP BY dept;
Если считаете проценты:
SELECT
ROUND(
paid_orders::numeric / total_orders * 100,
2
) AS paid_percent
FROM daily_stats;
Здесь ::numeric помогает избежать целочисленного деления и сделать расчёт более предсказуемым.
Частые ошибки
Первая ошибка — использовать double precision для денег.
Плохо:
CREATE TABLE payments (
id integer,
amount double precision
);
Лучше:
CREATE TABLE payments (
id integer,
amount numeric(12, 2)
);
Вторая ошибка — не понимать, как округляются половинки.
Перед важным расчётом проверьте:
SELECT
ROUND(2.5::numeric) AS numeric_25,
ROUND(2.5::double precision) AS double_25,
ROUND(-2.5::numeric) AS numeric_minus_25,
ROUND(-2.5::double precision) AS double_minus_25;
Третья ошибка — округлять слишком рано.
Обычно лучше сначала посчитать итог, а потом округлить:
SELECT ROUND(SUM(amount), 2) AS total_amount
FROM payments;
А не округлять каждую строку без необходимости:
SELECT SUM(ROUND(amount, 2)) AS total_amount
FROM payments;
Иногда второй вариант нужен по бизнес-правилам. Но он должен быть осознанным, а не случайным.
Четвёртая ошибка — округлять в базе, потом ещё раз в приложении.
Если база и приложение используют разные правила, вы получите редкие и неприятные расхождения.
Главное
ROUND округляет число до ближайшего целого или до нужного количества знаков после запятой.
Базовый пример:
SELECT ROUND(3.14159) AS result;
Для PostgreSQL важно помнить:
ROUND(2.5::numeric) даёт 3;
ROUND(-2.5::numeric) даёт -3;
numeric округляет половинки от нуля;
double precision может использовать другую стратегию, часто банковскую;
- деньги лучше хранить в
numeric, а не в double precision;
- не округляйте промежуточные значения без необходимости;
- для важных расчётов заранее зафиксируйте тип данных и правило округления.
ROUND — это не просто «сделать красиво». В отчётах, платежах, комиссиях и зарплатах округление становится частью бизнес-логики. Поэтому хороший SQL-разработчик не просто пишет ROUND, а понимает, какое число, какого типа и по какому правилу он округляет.
ROUNDкажется одной из самых простых функций в SQL: взяли дробное число — получили ближайшее целое.Например:
SELECT ROUND(3.14159) AS result;Результат:
Но за этой простотой прячется важная деталь: округление бывает разным. Особенно на числах вроде
2.5,3.5,-2.5, где значение стоит ровно посередине между двумя целыми.Для обычного отчёта это может быть мелочью. А вот для денег, комиссий, зарплат, лимитов и сверок с внешними системами одна «лишняя» копейка уже становится настоящей проблемой. Причём проблема неприятная: она появляется редко, выглядит случайной и долго не находится.
Разберём, как работает
ROUNDв PostgreSQL, чем отличаетсяnumericотdouble precision, почему деньги лучше не хранить в плавающей точке и почему нельзя переносить округление между PostgreSQL, MySQL и ClickHouse вслепую.Что делает ROUND
ROUNDокругляет число до ближайшего целого.Базовый синтаксис:
Пример:
SELECT ROUND(3.14159) AS a, ROUND(9.8) AS b, ROUND(10.2) AS c;Результат:
То есть функция смотрит на дробную часть и выбирает ближайшее целое число.
Если дробная часть меньше половины, число округляется вниз:
SELECT ROUND(4.4) AS result;Результат:
Если дробная часть больше половины, число округляется вверх:
SELECT ROUND(4.6) AS result;Результат:
А вот с ровной половиной начинается самое интересное.
Как ROUND округляет половинки в PostgreSQL
В PostgreSQL литерал вроде
2.5обычно воспринимается как значение точного типаnumeric.Для
numericфункцияROUNDокругляет половинки от нуля.Это значит:
2.5превращается в3;3.5превращается в4;-2.5превращается в-3;-3.5превращается в-4.Пример:
SELECT ROUND(3.14159) AS a, ROUND(2.5) AS b, ROUND(3.5) AS c, ROUND(-2.5) AS d, ROUND(-3.5) AS e;Результат:
Фраза «от нуля» звучит немного непривычно, но смысл простой.
Положительное число при половинке уходит дальше вправо:
Отрицательное число при половинке уходит дальше влево:
То есть число становится дальше от нуля.
ROUND для numeric и double precision
Главная ловушка в PostgreSQL: результат округления может зависеть от типа данных.
Для точного типа
numericполовинки округляются от нуля.А вот для
double precisionповедение может отличаться. Часто используется банковское округление: половинка уходит к ближайшему чётному числу.Сравним:
SELECT ROUND(2.5::numeric) AS numeric_25, ROUND(3.5::numeric) AS numeric_35, ROUND(2.5::double precision) AS double_25, ROUND(3.5::double precision) AS double_35;Результат на типичной системе может быть таким:
Почему
2.5::double precisionстало2, а не3?Потому что при банковском округлении ровная половинка округляется к ближайшему чётному числу.
Для
2.5соседи —2и3. Чётное число —2, поэтому результат2.Для
3.5соседи —3и4. Чётное число —4, поэтому результат4.Это не ошибка PostgreSQL. Это другая стратегия округления.
Зачем вообще нужно банковское округление
Обычное округление половинок от нуля кажется более привычным:
2.5стало3, и всё понятно.Но если округлять огромное количество чисел, постоянное округление половинок в одну сторону может давать систематическое смещение.
Банковское округление пытается это смещение уменьшить: иногда половинка округляется вниз, иногда вверх — в зависимости от ближайшего чётного числа.
Например:
На длинной дистанции такие округления меньше «тащат» сумму в одну сторону.
Но для разработчика важнее другое: нельзя просто сказать «я использую ROUND» и считать вопрос закрытым. Нужно понимать, какой тип данных вы округляете и какую стратегию использует ваша система.
Почему одно и то же число может округлиться по-разному
Посмотрите на этот запрос:
SELECT ROUND(2.5) AS a, ROUND(2.5::numeric) AS b, ROUND(2.5::double precision) AS c;Выглядит так, будто везде одно и то же число
2.5.Но для базы это не совсем одно и то же. В первом и втором случае PostgreSQL работает с точным числом, а в третьем — с приблизительным числом с плавающей точкой.
Именно поэтому результат может отличаться.
Особенно опасно, когда в запросе смешиваются разные типы:
SELECT ROUND(amount) AS rounded_amount FROM orders;Если
amountимеет типnumeric, вы получите одну стратегию. Еслиamountимеет типdouble precision, возможны другие нюансы.Для финансовых расчётов лучше не оставлять это на волю случая. Приводите данные к точному типу явно:
SELECT ROUND(amount::numeric) AS rounded_amount FROM orders;Но ещё лучше — изначально хранить деньги в
numeric, а не вdouble precision.Почему double precision опасен для денег
Тип
double precisionхранит числа приблизительно. Он быстрый и полезный для научных расчётов, измерений, координат, статистики, но для денег подходит плохо.Причина в том, что многие десятичные дроби нельзя точно представить в двоичной плавающей точке.
Для человека число выглядит как
19.99.А внутри оно может быть чем-то вроде:
Или наоборот:
Чаще всего это незаметно. Но при суммировании, расчётах комиссий и последующем округлении такие хвосты могут вылезти наружу.
Например:
SELECT SUM(amount) AS raw_total, ROUND(SUM(amount)) AS rounded_total FROM orders WHERE status = 'paid';Если
amount— этоnumeric(12,2), сумма считается точно.Если
amount— этоdouble precision, сумма может накопить маленькую погрешность. ИROUNDбудет округлять уже не красивое человеческое число, а число с техническим хвостом.Для денег правило простое: храните суммы в
numeric.Например:
CREATE TABLE payments ( id integer, amount numeric(12, 2) );Так вы заранее убираете целый класс странных проблем.
Пример: округлить среднюю зарплату
Допустим, есть таблица сотрудников:
CREATE TABLE employees ( id integer, dept text, salary numeric(12, 2) );Нужно посчитать среднюю зарплату по отделам и округлить до целого.
SELECT dept, ROUND(AVG(salary)) AS avg_salary FROM employees GROUP BY dept ORDER BY avg_salary DESC;Результат может быть таким:
Если
salaryхранится какnumeric, тоAVG(salary)тоже будет точным числовым результатом, иROUNDотработает предсказуемо.Это хороший пример нормального применения
ROUND: мы не меняем исходные данные, а готовим удобное значение для отчёта.ROUND — не всегда просто косметика
Иногда разработчики воспринимают округление как украшение результата: мол, просто убрали лишние знаки после запятой.
Но в бизнес-логике округление может быть частью расчёта.
Например:
Если база округляет одним способом, а приложение потом другим, могут появиться расхождения.
Например, база посчитала:
А приложение округлило похожее значение по банковскому правилу:
На одной строке это выглядит мелко. Но в отчёте на тысячи операций такие различия превращаются в вопросы от бухгалтерии, финансового отдела или внешней системы.
Поэтому для важных расчётов стоит заранее договориться:
Не округляйте слишком рано
Ещё одна частая ошибка — округлять каждую строку отдельно, а потом суммировать.
Например:
SELECT SUM(ROUND(amount)) AS total FROM orders;Это не то же самое, что сначала суммировать, а потом округлить:
SELECT ROUND(SUM(amount)) AS total FROM orders;Разница может быть заметной.
Представим три суммы:
Если округлить каждую отдельно:
Если сначала сложить:
А потом округлить:
Получились разные итоги.
Поэтому перед округлением задайте себе вопрос: нужно округлять каждую операцию отдельно или только общий результат?
В финансовых и аналитических отчётах это принципиально.
ROUND с количеством знаков после запятой
У
ROUNDесть вариант со вторым аргументом:Он округляет не до целого, а до нужного количества знаков после запятой.
Например:
SELECT ROUND(3.14159, 2) AS pi_2, ROUND(9.876, 1) AS value_1;Результат:
Это удобно для процентов, средних значений и денежных отчётов, где нужно показать две цифры после запятой.
Например:
SELECT dept, ROUND(AVG(salary), 2) AS avg_salary FROM employees GROUP BY dept;Но логика остаётся той же: важно понимать тип данных и стратегию округления.
ROUND и отображение результата
Иногда округление используют только для красивого вывода:
SELECT ROUND(conversion_rate * 100, 2) AS conversion_percent FROM daily_metrics;Это нормальный сценарий для отчёта.
Но не стоит путать округление для отображения и округление для хранения.
Если вы округлили значение в
SELECT, исходные данные в таблице не изменились.SELECT ROUND(amount, 2) AS amount_for_report FROM payments;Такой запрос просто показывает значение в удобном виде.
А вот если вы используете
ROUNDвUPDATE, вы уже меняете данные:UPDATE payments SET amount = ROUND(amount, 2);С такими запросами нужно быть осторожнее: после обновления вы можете потерять исходную точность.
MySQL: ROUND и типы данных
В MySQL функция тоже называется
ROUND.Базовый пример:
SELECT ROUND(2.5) AS result;Для точных типов вроде
DECIMALполовинки обычно округляются от нуля.Например:
SELECT ROUND(CAST(2.5 AS DECIMAL(10, 1))) AS result;Результат:
А вот для приблизительных типов вроде
DOUBLEповедение может зависеть от реализации и платформы. На практике можно встретить банковское округление.Например:
SELECT ROUND(CAST(2.5 AS DECIMAL(10, 1))) AS decimal_result, ROUND(2.5e0) AS double_result;На одной системе результат может выглядеть так:
Главный вывод такой же, как в PostgreSQL: для денег и точных бизнес-расчётов используйте точные типы, а не
DOUBLE.ClickHouse: ROUND и банковское округление
В ClickHouse тоже есть функция
round.Но при переносе логики из PostgreSQL нужно быть особенно внимательным: стратегия округления половинок может отличаться от привычной.
Например, банковское округление даёт такие результаты:
SELECT round(2.5) AS a, round(3.5) AS b;Результат может быть таким:
То есть
2.5округляется к2, потому что2— ближайшее чётное. А3.5округляется к4, потому что4— ближайшее чётное.В ClickHouse также есть функция
roundBankers, которая явно подчёркивает банковскую стратегию:SELECT roundBankers(2.5) AS a, roundBankers(3.5) AS b;Результат:
Практическое правило: если переносите расчёты между PostgreSQL, MySQL и ClickHouse, обязательно проверьте контрольные значения:
И отдельно проверьте типы: точные числа и числа с плавающей точкой могут вести себя по-разному.
Как безопасно использовать ROUND в отчётах
Для обычных отчётов можно держаться такого шаблона.
Если считаете деньги:
SELECT ROUND(SUM(amount), 2) AS total_amount FROM payments WHERE status = 'paid';При этом
amountлучше хранить какnumeric.Если считаете средние значения:
SELECT dept, ROUND(AVG(salary)) AS avg_salary FROM employees GROUP BY dept;Если считаете проценты:
SELECT ROUND( paid_orders::numeric / total_orders * 100, 2 ) AS paid_percent FROM daily_stats;Здесь
::numericпомогает избежать целочисленного деления и сделать расчёт более предсказуемым.Частые ошибки
Первая ошибка — использовать
double precisionдля денег.Плохо:
CREATE TABLE payments ( id integer, amount double precision );Лучше:
CREATE TABLE payments ( id integer, amount numeric(12, 2) );Вторая ошибка — не понимать, как округляются половинки.
Перед важным расчётом проверьте:
SELECT ROUND(2.5::numeric) AS numeric_25, ROUND(2.5::double precision) AS double_25, ROUND(-2.5::numeric) AS numeric_minus_25, ROUND(-2.5::double precision) AS double_minus_25;Третья ошибка — округлять слишком рано.
Обычно лучше сначала посчитать итог, а потом округлить:
SELECT ROUND(SUM(amount), 2) AS total_amount FROM payments;А не округлять каждую строку без необходимости:
SELECT SUM(ROUND(amount, 2)) AS total_amount FROM payments;Иногда второй вариант нужен по бизнес-правилам. Но он должен быть осознанным, а не случайным.
Четвёртая ошибка — округлять в базе, потом ещё раз в приложении.
Если база и приложение используют разные правила, вы получите редкие и неприятные расхождения.
Главное
ROUNDокругляет число до ближайшего целого или до нужного количества знаков после запятой.Базовый пример:
SELECT ROUND(3.14159) AS result;Для PostgreSQL важно помнить:
ROUND(2.5::numeric)даёт3;ROUND(-2.5::numeric)даёт-3;numericокругляет половинки от нуля;double precisionможет использовать другую стратегию, часто банковскую;numeric, а не вdouble precision;ROUND— это не просто «сделать красиво». В отчётах, платежах, комиссиях и зарплатах округление становится частью бизнес-логики. Поэтому хороший SQL-разработчик не просто пишетROUND, а понимает, какое число, какого типа и по какому правилу он округляет.