Числовые типы: integer, bigint, numeric

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

Числовой тип определяет диапазон значений и точность хранения числа; правильный выбор экономит память и защищает от ошибок округления.

Целые числа

PostgreSQL предлагает три целочисленных типа, отличающихся диапазоном и размером.

ТипРазмерДиапазон (примерно)
smallint2 байта±32 тысячи
integer (int)4 байта±2 миллиарда
bigint8 байт±9 квинтиллионов

В большинстве случаев берут integer — его хватает для счётчиков, количеств и большинства id. bigint нужен там, где значения реально большие: идентификаторы в крупных системах, количество байт, временные метки в миллисекундах.

Точные дробные: numeric

Для денег и любых величин, где нельзя терять копейки, существует numeric(precision, scale) (синоним — decimal). Это число с фиксированной точностью без ошибок округления.

-- numeric(10, 2): всего 10 значащих цифр, 2 после запятой
price NUMERIC(10, 2)   -- хранит, например, 18000.00 точно
-- Деньги ВСЕГДА храните в numeric, а не во float!

Запись numeric(10, 2) означает: до 10 цифр всего, из них 2 после десятичной точки, то есть до 99 999 999.99.

Приближённые дробные: real и double precision

Типы real (4 байта) и double precision (8 байт) — это числа с плавающей точкой. Они быстрые и компактные, но хранят значения приближённо. Для научных расчётов это нормально, для денег — недопустимо.

-- ВАЖНО: голые литералы 0.1, 0.2, 0.3 в PostgreSQL имеют тип numeric,
-- поэтому без явного приведения сравнение даст true.
-- Чтобы увидеть ловушку плавающей точки, приводим к float8:
SELECT 0.1::float8 + 0.2::float8 = 0.3::float8 AS float_equal;

Вывод:

float_equal
f

Результат f (ложь!) — классическая ловушка плавающей точки: для double precision сумма 0.1 + 0.2 не равна ровно 0.3. (А вот SELECT 0.1 + 0.2 = 0.3 без приведения вернёт t — ведь литералы здесь numeric, а не float.) Именно поэтому деньги хранят в numeric, где такой проблемы нет.

Практика: считаем суммы в песочнице

Создадим таблицу заказов с целыми ценами и посчитаем итог. Пример переносимый — запустите и поменяйте числа.

CREATE TABLE orders (
    id     INTEGER PRIMARY KEY,
    item   TEXT,
    qty    INTEGER,
    price  INTEGER
);

INSERT INTO orders (id, item, qty, price) VALUES
    (1, 'Кофе', 3, 250),
    (2, 'Чай', 2, 180),
    (3, 'Печенье', 5, 90);

SELECT item, qty * price AS total
FROM orders
ORDER BY total DESC;

Вывод:

Кофе|750
Печенье|450
Чай|360

Здесь total вычисляется на лету как qty * price и сортируется по убыванию. Поскольку цены целые, всё точно и без округлений.

Итог

  • integer — рабочая лошадка для счётчиков и id; bigint — для очень больших значений.
  • numeric(p, s) — точные дроби; деньги храните только в нём.
  • real/double precision быстрые, но приближённые — не для финансов.
Проверьте себя
1. В каком типе следует хранить денежные суммы?
Areal
Bdouble precision
Cnumeric
Dinteger всегда
2. Когда вместо integer стоит взять bigint?
AКогда нужны дробные значения
BКогда нужна точность до копеек
CНикогда, integer всегда лучше
DКогда значения могут превысить ±2 миллиарда
3. Почему выражение 0.1 + 0.2 = 0.3 для float даёт «ложь»?
AЧисла с плавающей точкой хранятся приближённо
BОшибка в PostgreSQL
C0.3 — недопустимое число
DНужно писать 0,3 через запятую