Learning Platform
Глоссарий Troubleshooting
Урок 08.01 · 23 мин
Средний
sqltypesdecimaltimestamp

Система типов: примитивы, DECIMAL, TIMESTAMP, тип number

Trino понимает ANSI SQL, но он движок запросов без собственного хранилища — и это формирует его систему типов особым образом. Тип в Trino — это не описание того, как данные лежат на диске (за раскладку отвечает источник: Parquet, ORC, строки PostgreSQL). Тип в Trino — это контракт обработки в памяти: как значение представлено в Block’е, какие операции над ним определены, как оно сериализуется в exchange между воркерами.

Числовые типы SQL: INT, BIGINT, NUMERIC — почему деньги не FLOAT Текст и время: TEXT, TIMESTAMP, часовые пояса

Этот урок открывает модуль про SQL в Trino. Мы разберём примитивные типы, затем подробно — два, где скрыта вся тонкость: DECIMAL с его границей точности и TIMESTAMP с параметром precision. И отдельно — тип number, появившийся в релизе 480 как ответ на фундаментальное ограничение DECIMAL.


Примитивные типы: карта

Trino делит примитивы на несколько семейств. Зафиксируем их таблицей — это рабочая карта, к которой вы будете возвращаться.

СемействоТипыЗаметка
ЛогическийBOOLEANtrue / false
ЦелочисленныеTINYINT, SMALLINT, INTEGER, BIGINT8, 16, 32, 64 бита со знаком
Плавающая точкаREAL, DOUBLE32 и 64 бита IEEE 754
Точные дробныеDECIMAL(p, s)фиксированная точность и масштаб
СтрокиVARCHAR(n), CHAR(n), VARBINARYтекст и двоичные данные
Дата и времяDATE, TIME, TIMESTAMP, с WITH TIME ZONE и без, INTERVALпараметризуются precision
ПрочееUUID, IPADDRESS, JSON, HYPERLOGLOGспециализированные

Целочисленные и плавающие типы прямолинейны и совпадают по семантике с тем, что вы знаете из других СУБД. BIGINT — рабочая лошадка для целых; INTEGER экономит память, когда диапазона хватает. DOUBLE — для научных вычислений, где приемлема погрешность представления. На двух семействах остановимся подробно, потому что именно там инженеры допускают ошибки.


DECIMAL: точность ценой границы

DECIMAL(p, s) хранит число с фиксированной точностью: p (precision) — общее число значащих десятичных цифр, s (scale) — сколько из них после запятой. DECIMAL(10, 2) — это до 10 цифр всего, 2 после запятой: максимум 99999999.99.

Зачем он нужен, если есть DOUBLE. DOUBLE — это IEEE 754, двоичная плавающая точка. Многие десятичные дроби в двоичной системе непредставимы точно — классический пример, 0.1 в DOUBLE хранится с крошечной погрешностью. Для денег это недопустимо: суммирование миллиона транзакций накопит ошибку, и баланс не сойдётся на копейки. DECIMAL хранит число как масштабированное целое и потому представляет десятичные дроби точно. Правило простое: деньги, ставки, всё, где «должно сходиться до копейки», — DECIMAL, не DOUBLE.

SELECT DECIMAL '0.1' + DECIMAL '0.2' AS dec_sum,
       DOUBLE '0.1' + DOUBLE '0.2'  AS dbl_sum;
 dec_sum |        dbl_sum
---------+------------------------
 0.3     | 0.30000000000000004

Видно ровно проблему: DECIMAL даёт точное 0.3, DOUBLE — приближение с хвостом.

Но у DECIMAL есть жёсткая граница. Максимальная precision — 38 значащих цифр. Это не произвол: Trino представляет DECIMAL точностью до 18 цифр в одном 64-битном long, а от 19 до 38 — в 128-битном представлении. 38 цифр — это потолок 128-битного целого. Свыше — некуда положить.

Для подавляющего большинства задач 38 цифр с запасом хватает. Но есть области, где не хватает: научные расчёты с очень большими или очень малыми величинами, криптография, финансовая математика с длинными промежуточными произведениями, агрегаты, где промежуточный результат перерастает 38 цифр ещё до округления. Здесь DECIMAL упирается в стену, а DOUBLE неприемлем из-за погрешности.

DECIMAL: точно, но до 38 цифр
precision до 18DECIMAL точностью до 18 значащих цифр Trino хранит в одном 64-битном long — компактно и быстро
нужно больше цифр
precision 19-38От 19 до 38 цифр DECIMAL хранится в 128-битном представлении
нужно больше 38
свыше 38128-битное целое исчерпано — DECIMAL дальше не масштабируется. Здесь и нужен тип number

Тип number: высокоточная десятичная арифметика

Чтобы пробить эту стену, в релизе 480 (24 марта 2026) в Trino добавили тип number. Его легко принять за «обобщённый числовой примитив» — это ошибка. number — это узко-специализированный тип: высокоточное десятичное число переменного масштаба.

Разберём по частям, чем number отличается от DECIMAL.

Высокая точность. number снимает потолок в 38 цифр. Он держит примерно до 200 значащих десятичных цифр — на порядки больше DECIMAL. Это и есть прямой ответ на «стену DECIMAL» из предыдущего раздела.

Переменный масштаб. У DECIMAL масштаб фиксирован в типе: DECIMAL(10, 2) — всегда ровно 2 знака после запятой, тип статически параметризован (p, s). У number масштаб не зашит в тип — он переменный, значение само несёт свою точность и масштаб. Не нужно заранее объявлять (p, s): результат деления или произведения сохраняет столько знаков, сколько требуется.

Опора на BigDecimal. Под капотом number реализован поверх Java-класса BigDecimal — стандартного представления произвольной точности на JVM. Отсюда и возможности, и цена: BigDecimal — это объект в куче, не примитив; арифметика над ним кратно дороже арифметики над long. number — не бесплатная замена DECIMAL, а инструмент для случаев, где точности DECIMAL физически не хватает.

Infinity и NaN. В отличие от DECIMAL, number поддерживает специальные значения — положительную и отрицательную бесконечность и NaN (not-a-number). Это сближает его модель со стандартом плавающей точки в части особых значений, но без потери точности на обычных числах: number остаётся десятичным и точным.

DECIMAL против number
DECIMAL(p, s)Фиксированная точность до 38 цифр и фиксированный масштаб, зашитый в тип. Компактное представление в long или 128 битах, быстрая арифметика
когда 38 цифр мало
numberВысокая точность до примерно 200 цифр, переменный масштаб, поддержка Infinity и NaN. Поверх Java BigDecimal — объект в куче, арифметика дороже
WARNING

number — не «улучшенный универсальный числовой тип» и не замена DECIMAL по умолчанию. Это специализированный тип для высокоточной десятичной арифметики там, где 38 цифр DECIMAL физически не хватает: длинные финансовые расчёты с большими промежуточными произведениями, научные величины экстремального порядка. За точность платят: BigDecimal под капотом — объект в куче, арифметика над ним кратно медленнее, чем над DECIMAL. Для денег и обычных дробей по-прежнему берите DECIMAL; number доставайте, только когда упёрлись в его потолок.


TIMESTAMP и параметр precision

Второе место, где прячется тонкость, — типы даты и времени. Главная идея: TIMESTAMP в Trino параметризуется точностьюTIMESTAMP(p), где p — число знаков дробной части секунды.

  • TIMESTAMP(0) — без долей секунды.
  • TIMESTAMP(3) — миллисекунды.
  • TIMESTAMP(6) — микросекунды; это значение по умолчанию, когда пишут просто TIMESTAMP.
  • TIMESTAMP(9) — наносекунды.
  • TIMESTAMP(12) — пикосекунды; верхняя граница.

Почему это важно на практике. Точность — часть типа, и она участвует в сравнениях и в чтении из источников. Источники хранят время с разной точностью: Parquet традиционно — миллисекунды или микросекунды, Iceberg v3 ввёл наносекундные timestamp’ы. Если объявить колонку с одной точностью, а данные приходят с другой, происходит приведение, и при сужении точности — усечение долей. Понимать p нужно, чтобы не потерять разряды молча.

Ортогонально точности — наличие или отсутствие часового пояса:

ТипЧто хранит
TIMESTAMP(p)момент без привязки к зоне — «настенное время»
TIMESTAMP(p) WITH TIME ZONEмомент вместе с часовым поясом — однозначная точка на оси времени

Различие принципиально. TIMESTAMP WITH TIME ZONE задаёт абсолютный момент: его можно сравнивать между поясами без двусмысленности. TIMESTAMP без зоны — это «настенное время», и 12:00 в нём не отвечает на вопрос «в каком часовом поясе». Для событийных данных в распределённой системе почти всегда нужен вариант WITH TIME ZONE.

SELECT TIMESTAMP '2026-05-20 14:30:00.123456'        AS ts_micro,
       TIMESTAMP '2026-05-20 14:30:00.1'             AS ts_short,
       TIMESTAMP '2026-05-20 14:30:00 Europe/Berlin' AS ts_with_zone;
          ts_micro          |        ts_short         |             ts_with_zone
----------------------------+-------------------------+--------------------------------------
 2026-05-20 14:30:00.123456 | 2026-05-20 14:30:00.100 | 2026-05-20 14:30:00 Europe/Berlin

Литерал 14:30:00.123456 дал precision 6 (микросекунды), 14:30:00.1 — precision 3 после нормализации отображения. Точность вывелась из самого литерала.


Приведение типов: CAST и его границы

Trino строг к типам: операторы и функции требуют совместимых типов на входе, и неявных приведений меньше, чем в некоторых СУБД. Явное приведение — функция CAST(value AS type). Есть и TRY_CAST — возвращает NULL вместо ошибки, если приведение невозможно.

SELECT CAST('2026-05-20' AS DATE)         AS to_date,
       CAST(42 AS DECIMAL(5,2))           AS to_decimal,
       TRY_CAST('не число' AS INTEGER)    AS bad_cast;
  to_date   | to_decimal | bad_cast
------------+------------+----------
 2026-05-20 |     42.00  |   NULL

Где CAST теряет данные. Приведение DECIMAL(10,4) к DECIMAL(10,2) усечёт два знака. Приведение TIMESTAMP(9) к TIMESTAMP(3) отбросит микро- и наносекунды. Приведение BIGINT, не влезающего в INTEGER, даст ошибку переполнения. Тип в Trino — контракт, и CAST — единственная санкционированная точка его смены; делать это нужно осознанно, понимая, теряются ли разряды.


Попробуй сам

На песочнице курса (Trino 481):

  1. Выполните SELECT DECIMAL '0.1' + DECIMAL '0.2', DOUBLE '0.1' + DOUBLE '0.2'; и зафиксируйте разницу. Затем SELECT typeof(DECIMAL '1.5'), typeof(DOUBLE '1.5'), typeof(CURRENT_TIMESTAMP); — функция typeof показывает точный тип выражения, включая precision. Объясните, какую precision получил CURRENT_TIMESTAMP и почему.

  2. Подумайте над сценарием для типа number. Финансовая модель перемножает 30 множителей подряд, каждый — DECIMAL(20,10). Прикиньте, сколько значащих цифр может набрать промежуточное произведение и почему DECIMAL(38, s) тут переполнится. Сформулируйте в двух предложениях, почему именно number (а не DOUBLE) — корректный выбор: одно про точность, одно про границу 38 цифр.

  3. Создайте две версии одной мысли про TIMESTAMP. Выполните SELECT CAST(TIMESTAMP '2026-05-20 10:00:00.123456789' AS TIMESTAMP(3)); и объясните, что произошло с наносекундами и почему это молчаливая потеря данных, а не ошибка.


Проверка знанийKnowledge check
Чем тип number отличается от DECIMAL, какую конкретную проблему DECIMAL он решает, и почему его не стоит использовать как замену DECIMAL по умолчанию?
ОтветAnswer
number — это высокоточный десятичный тип переменного масштаба, добавленный в Trino в релизе 480. Он отличается от DECIMAL по трём осям. Точность: DECIMAL ограничен 38 значащими цифрами, потому что хранится в 64-битном long (до 18 цифр) или 128-битном представлении (19-38), и 38 — потолок 128-битного целого; number держит примерно до 200 цифр. Масштаб: у DECIMAL он фиксирован в типе через (p, s), у number — переменный, значение само несёт свою точность, заранее объявлять (p, s) не нужно. Особые значения: number поддерживает Infinity и NaN, DECIMAL — нет. Конкретная проблема, которую number решает, — это стена в 38 цифр DECIMAL: длинные финансовые расчёты с большими промежуточными произведениями, научные величины экстремального порядка, агрегаты, где промежуточный результат перерастает 38 цифр. DOUBLE в таких случаях неприемлем из-за двоичной погрешности. Заменой DECIMAL по умолчанию number быть не должен, потому что под капотом он реализован поверх Java BigDecimal — это объект в куче, не примитив, и арифметика над ним кратно дороже арифметики над компактным DECIMAL в long или 128 битах. Для денег и обычных дробей берут DECIMAL; number достают только когда упёрлись в его потолок точности.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Почему для денежных сумм в Trino выбирают DECIMAL, а не DOUBLE?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 7