Раздел 33 · PostgreSQL с нуля

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

junior~20 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

В прошлом уроке мы создали первую таблицу и выбрали типы колонок наугад. Пора разобраться. Начнём с чисел: в книжном магазине они повсюду, от количества страниц до цены. И у каждого числа своя правильная «коробка».

Коробки разного размера

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

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

В PostgreSQL числовые типы делятся на три семьи:

  • целые для того, что считают штуками: страницы, остатки на складе, идентификаторы;
  • точные дробные для денег и всего, где копейка важна;
  • приблизительные дробные для измерений, где важна скорость, а не последний знак.

Целые: smallint, integer, bigint

ТипБайтДиапазон
smallint2от −32 768 до 32 767
integer4примерно ± 2,1 миллиарда
bigint8примерно ± 9,2 квинтиллиона

integer (его же пишут int или int4) это выбор по умолчанию. bigint (int8) берут, когда счёт может уйти за два миллиарда: идентификаторы больших таблиц, просмотры, суммы в копейках. smallint (int2) экономит два байта, но на практике его выбирают редко: выигрыш копеечный, а риск упереться в потолок реальный.

Что будет, если число не влезло? PostgreSQL не обрезает его молча, а останавливает запрос с ошибкой:

SELECT 2147483647 + 1;
ERROR:  integer out of range

Это называется переполнением. Java и Go при переполнении молча «перескакивают» на отрицательные числа, и ошибка всплывает через месяц в отчёте. Postgres честно падает сразу. Если же один из операндов bigint, сложение пройдёт:

SELECT 2147483647::bigint + 1;
  ?column?
------------
 2147483648

Запись ::bigint это приведение типа, ему посвящён урок 8. Пока читай её как «считай это число bigint’ом».

Есть и ловушка: деление целого на целое даёт целое, дробная часть отбрасывается.

SELECT 10 / 3, 10 / 3.0;
 ?column? |      ?column?
----------+--------------------
        3 | 3.3333333333333333

Стоит одному из чисел стать дробным, и результат тоже дробный.

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

Цена книги 990 рублей 50 копеек. Для денег в Postgres есть numeric: он хранит число цифра в цифру, как ты пишешь его на бумаге.

У numeric два необязательных параметра: numeric(точность, масштаб).

  • Точность это сколько цифр всего.
  • Масштаб это сколько из них после запятой.

numeric(10, 2) вмещает до 99 999 999,99: восемь цифр до запятой и две после. Лишние знаки после запятой округляются, а лишние цифры до запятой вызывают ошибку:

SELECT 1234.567::numeric(6, 2);
SELECT 123456.7::numeric(6, 2);
 numeric
---------
 1234.57

ERROR:  numeric field overflow
DETAIL:  A field with precision 6, scale 2 must round to an absolute value less than 10^4.

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

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

урок из курса

Урок из курса «PostgreSQL с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.