Решение: выбор правильного типа данных
Выбор типа данных — одно из самых дорогих для последующего исправления решений: неверный тип даёт неверные результаты (например, float для денег), лишнее место и медленные запросы. Это дерево ведёт к правильному типу для числовых, текстовых полей, даты/времени, ключа (PK), полуструктурированных (JSON) и категориальных (ENUM) полей и указывает на классические ловушки, такие как деньги и кодировки.
Какие данные вы храните?
Правильный тип определяется природой данных. К какой категории относится поле: число, текст, дата/время, первичный ключ таблицы, гибкие/полуструктурированные (JSON) данные или ограниченный набор категорий?
- Числовое значение (количество, деньги, измерение, счётчик)
- Текст (имя, описание, код)
- Дата и/или время
- Я проектирую первичный ключ (PK)
- Гибкие/вложенные/переменные поля (полуструктурированные данные)
- Ограниченный известный набор категорий (статус, тип, приоритет)
Число целое или дробное — и это деньги?
Для чисел самое важное различие — точность. Значение целое (количество, ID, счётчик) или дробное? Если дробное, это ДЕНЬГИ/финансовая сумма? Двоичная плавающая точка (FLOAT/DOUBLE) никогда не используется для денег и везде, где требуется точность, так как даёт ошибки округления.
- Целое (количество, счётчик, id)
- Деньги или точное десятичное (цена, баланс, ставка)
Рекомендация: целое (SMALLINT / INT / BIGINT)
Для целых выбирайте наименьший тип, покрывающий нужный диапазон. Неоправданно большой тип тратит место и увеличивает размер индекса; но для растущих полей, таких как счётчики/PK, достаточная разрядность обязательна, чтобы избежать переполнения.
Рекомендация: NUMERIC / DECIMAL — НИКОГДА не FLOAT для денег
Для денег и точных десятичных значений используйте NUMERIC/DECIMAL с фиксированной точкой (точные). FLOAT/DOUBLE — двоичная плавающая точка, они не могут точно хранить значения вроде 0.1; сложение/умножение даёт расхождения в копейках, неверные балансы и проблемы при аудите.
Рекомендация: текст — VARCHAR vs TEXT vs CHAR
Для текста используют VARCHAR/TEXT при переменной длине и CHAR при действительно фиксированной длине. В современных СУБД разница в хранении между VARCHAR и TEXT в основном пренебрежимо мала; реальное решение — вводить ли смысловое ограничение (ограничение длины).
Рекомендация: дата/время — TIMESTAMP с часовым поясом
Если вы храните момент времени (время события), выбирайте тип с учётом часового пояса. Хранение без часового пояса ведёт к неверным вычислениям и сравнениям между регионами/при переходе на летнее время.
Рекомендация: первичный ключ — BIGINT vs UUID
Для PK есть два основных варианта: возрастающее число (BIGINT identity/sequence) или UUID. Числовой PK мал и удобен для индекса, но предсказуем и требует централизованной генерации; UUID можно генерировать распределённо, но он больше, а его случайный порядок может вредить индексу.
Рекомендация: JSON / JSONB — используйте умеренно
Тип JSON удобен для полей с гибкой схемой, вложенных или разреженных полей; но реляционная дисциплина (ограничения, индексы, типобезопасность) может быть утрачена. Правило: выносите постоянные и часто запрашиваемые поля в обычные столбцы, в JSON держите только действительно изменчивую часть.
Рекомендация: ENUM vs справочная (lookup) таблица
Для ограниченных наборов категорий есть два варианта: тип ENUM СУБД или справочная таблица + внешний ключ. ENUM компактен и прост, но добавление значения требует изменения схемы; справочная таблица гибка и может нести дополнительные атрибуты (описание, флаг активности, порядок).