Числовые типы: 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 байт) — это числа с плавающей точкой. Они быстрые и компактные, но хранят значения приближённо. Для научных расчётов это нормально, для денег — недопустимо.

SELECT 0.1 + 0.2 = 0.3 AS float_equal;

Вывод:

float_equal
0

Результат 0 (ложь!) — классическая ловушка плавающей точки: 0.1 + 0.2 не равно ровно 0.3. Именно поэтому деньги хранят в 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Когда значения могут превысить ±2 миллиарда
CКогда нужна точность до копеек
DНикогда, integer всегда лучше
3. Почему выражение 0.1 + 0.2 = 0.3 для float даёт «ложь»?
AОшибка в PostgreSQL
BЧисла с плавающей точкой хранятся приближённо
C0.3 — недопустимое число
DНужно писать 0,3 через запятую
Поддержать проект