deшка
Урок 10.3

Типы данных: точность, отсутствие и повторяемость

Как выбрать UInt, Decimal, дату, Nullable, LowCardinality и составные типы под смысл данных.

Тип столбца определяет допустимые значения, способ хранения и операции над ними. В аналитической базе это влияет не только на правильность, но и на объём распаковки: миллиард значений UInt64 и UInt8 занимают разный объём до сжатия.

Числа по смыслу и диапазону

UInt — беззнаковое целое, Int — целое со знаком. Число в имени обозначает количество бит. Например, UInt8 хранит 0–255, UInt32 — 0–4 294 967 295. Для количества строк используем UInt64: отрицательное значение не имеет смысла, а объём может быть большим. Для коррекции-слагаемого, которое бывает отрицательным, нужен знаковый тип.

Размер целого типа выбирают по диапазону источника. Если длительность в миллисекундах может превышать предел UInt32, тип нужно расширить.

Float32 и Float64 хранят приближённые двоичные числа. Они удобны для измерений и статистики, но не гарантируют точное десятичное представление суммы денег. Decimal(P, S) хранит десятичное число с фиксированным масштабом: P — общее количество цифр, S — количество после точки.

SELECT
    toDecimal64('12.30', 2) + toDecimal64('0.20', 2) AS amount,
    toTypeName(amount) AS type;

Результат суммы — 12.50. Строки в преобразовании задают десятичные значения без промежуточного Float. При умножении масштаб результата может вырасти; правила округления при делении задают явно.

Дата, время и часовой пояс

Date хранит календарную дату. DateTime — время с точностью до секунды. DateTime64(3, 'UTC') поддерживает миллисекунды; 3 задаёт количество десятичных разрядов дробной секунды.

SELECT
    toDateTime('2026-01-01 09:00:00', 'UTC') AS finished,
    toTimeZone(finished, 'Europe/Moscow') AS local_time,
    toDate(finished) AS utc_day;

Один момент отображается как 09:00 UTC и 12:00 в Москве. Часовой пояс является частью типа, а не отдельной меткой в каждой строке. Если источник передаёт времена без зоны, сначала нужно выяснить их смысл. Дневная группировка в UTC и локальной зоне может дать разные календарные дни.

Для дневных отчётов секунд может быть достаточно. В логах с высокой частотой событий может понадобиться DateTime64.

Nullable сохраняет отсутствие значения

Nullable(UInt32) допускает NULL в дополнение к обычным числам. ClickHouse хранит признак отсутствия отдельной маской, поэтому чтение и вычисления получают дополнительную работу.

Однако заменять неизвестную длительность на 0 ради скорости опасно: среднее начнёт учитывать выдуманный нулевой запуск. Агрегатные функции обычно пропускают NULL; count() считает строки, count(duration) — известные значения.

measurement_idduration
1100
2NULL
3200

Среднее известных длительностей равно 150. После подмены NULL на 0 получилось бы 100: в расчёт попало бы выдуманное нулевое измерение. Nullable сохраняет различие между неизвестным значением и нулём.

LowCardinality кодирует повторяющиеся значения

Кардинальность — число различных значений. У статуса может быть несколько вариантов, у категории — десятки, у уникального идентификатора — столько, сколько строк. Чем больше уникальных значений при том же числе строк, тем выше кардинальность.

LowCardinality(String) хранит словарь строк и коды, ссылающиеся на словарь. Вместо многократного хранения 'billing' можно хранить компактные коды. Некоторые вычисления тоже работают с этим представлением эффективнее.

LowCardinality удобен для статусов, категорий и повторяющихся названий. При почти уникальных значениях стоимость словаря возрастает; выбор проверяют на реальных данных и запросах.

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

Массивы, Map и JSON

Array(String) хранит список значений, Tuple — набор полей, Map(String, String) — пары ключ–значение. Массив удобен для меток запуска:

SELECT
    ['daily', 'backfill'] AS tags,
    has(tags, 'backfill') AS is_backfill,
    tags[1] AS first_tag;

Индексы массивов в ClickHouse начинаются с 1. arrayJoin разворачивает массив в строки и может изменить число строк перед агрегацией.

Тип JSON описывает значение столбца, формат JSONEachRow — входные строки. Современный JSON может выделять пути в подколонки и полезен при меняющейся структуре. Известные обязательные поля проще хранить типизированными колонками. Для документа, который сохраняют и возвращают целиком, может хватить String.

toUInt32OrNull выделяет некорректный текст, после чего NULL обрабатывают явно. OrZero заменяет ошибку нулём и может скрыть дефект источника. Отдельно проверяют диапазон и смысл преобразованных значений.

Войти, чтобы отмечатьдальше →
10.4 →Сжатие, кодирование и кодеки