NodeJS Back-end Инженер

NodeJS Back-end Инженер

Роадмап навыков для прокачки

Что такое UNSIGNED и что он даёт?

DatabasesSQLTypes

Основная идея

UNSIGNED — это модификатор числового типа, который запрещает хранение отрицательных значений и за счёт этого удваивает максимальный диапазон положительных чисел.

Ключевые аспекты

  • Только положительные числа — диапазон начинается с 0, а не с отрицательного значения
  • Удвоение диапазона — INT хранит до 2.1 млрд, INT UNSIGNED — до 4.3 млрд
  • Тот же размер — UNSIGNED не увеличивает размер хранения в байтах
  • Семантическая ясность — ID, возраст, количество не могут быть отрицательными

Плюсы использования UNSIGNED

  • Больший диапазон положительных значений без увеличения размера
  • Защита от случайной вставки отрицательных значений
  • Более понятная семантика данных
  • Оптимизация для AUTO_INCREMENT полей

Минусы

  • Не поддерживается в PostgreSQL (нужны CHECK constraints)
  • Ошибки при вычитании: 5 - 10 вызовет ошибку или переполнение
  • Сложности при миграции между СУБД

Частые ошибки на собеседованиях

  • Путают UNSIGNED с увеличением размера типа — размер остаётся прежним
  • Забывают про арифметические ошибки при вычитании
  • Не знают, что PostgreSQL не поддерживает UNSIGNED напрямую
  • Считают UNSIGNED обязательным для PRIMARY KEY — это рекомендация, не требование

Введение и проблематика

При работе с числовыми данными в базе данных часто возникают ситуации, когда значения по определению не могут быть отрицательными: идентификаторы, возраст, количество товаров, просмотры. Модификатор UNSIGNED позволяет явно указать это ограничение и получить дополнительные преимущества.

UNSIGNED удваивает максимальное положительное значение без увеличения размера хранения. INT UNSIGNED может хранить числа до 4 294 967 295 вместо 2 147 483 647.


Базовая теория

Как работает UNSIGNED

В компьютере числа хранятся в двоичном формате. Для signed (знаковых) чисел один бит используется для хранения знака (+ или -). UNSIGNED использует все биты для хранения величины числа.

graph TD A[INT - 32 бита] --> B[Signed] A --> C[Unsigned] B --> D[1 бит знака + 31 бит значения] C --> E[32 бита значения] D --> F[-2 147 483 648 ... +2 147 483 647] E --> G[0 ... 4 294 967 295]

Сравнение диапазонов

ТипSigned диапазонUnsigned диапазон
TINYINT-128 ... 1270 ... 255
SMALLINT-32 768 ... 32 7670 ... 65 535
INT-2 147 483 648 ... 2 147 483 6470 ... 4 294 967 295
BIGINT-9.2 × 10¹⁸ ... 9.2 × 10¹⁸0 ... 1.8 × 10¹⁹

Практические примеры

Типичные сценарии использования UNSIGNED

sql
-- ID не может быть отрицательным
-- UNSIGNED удваивает максимальное количество записей
CREATE TABLE users (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL
);
 
-- С UNSIGNED: до 4.3 млрд записей
-- Без UNSIGNED: до 2.1 млрд записей

Объявление UNSIGNED в разных СУБД

sql
-- MySQL полностью поддерживает UNSIGNED
CREATE TABLE example (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    counter BIGINT UNSIGNED DEFAULT 0
);

Пограничные кейсы

⚠️

Арифметика с UNSIGNED может давать неожиданные результаты при вычитании.

Проблема вычитания

sql
-- MySQL с UNSIGNED
SET @a = CAST(5 AS UNSIGNED);
SET @b = CAST(10 AS UNSIGNED);
 
-- Ожидаем: -5
-- Получаем: ошибку или огромное положительное число!
SELECT @a - @b;
 
-- Результат зависит от sql_mode:
-- С STRICT_TRANS_TABLES: ошибка
-- Без: переполнение (очень большое число)

Решение проблемы вычитания

sql
-- Вариант 1: приведение к signed перед вычитанием
SELECT CAST(@a AS SIGNED) - CAST(@b AS SIGNED);
 
-- Вариант 2: использование GREATEST для защиты
SELECT GREATEST(0, @a - @b);
 
-- Вариант 3: проверка перед вычитанием
SELECT IF(@a >= @b, @a - @b, 0);
🚫

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

Когда НЕ использовать UNSIGNED

sql
-- ❌ Баланс счёта (может быть отрицательным — овердрафт)
balance INT UNSIGNED  -- Плохо!
balance INT           -- Хорошо
 
-- ❌ Разница температур
temp_diff INT UNSIGNED  -- Плохо!
temp_diff SMALLINT      -- Хорошо
 
-- ❌ Координаты (могут быть отрицательными)
latitude DECIMAL UNSIGNED   -- Плохо!
latitude DECIMAL(10, 8)     -- Хорошо

Преимущества UNSIGNED для PRIMARY KEY

sql
-- AUTO_INCREMENT с UNSIGNED
CREATE TABLE orders (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
 
-- Преимущества:
-- 1. Вдвое больше возможных ID (4.3 млрд vs 2.1 млрд)
-- 2. Семантически корректно (ID не бывает отрицательным)
-- 3. Небольшая оптимизация сравнений

Плюсы и минусы

АспектПлюсыМинусы
ДиапазонУдвоение положительных значенийПотеря отрицательных
СемантикаЯвное указание ограниченияТребует понимания
РазмерБез изменений
ПереносимостьPostgreSQL/SQL Server не поддерживают
АрифметикаПроблемы с вычитанием

Вопросы интервьюера

Q: Увеличивает ли UNSIGNED размер хранения?

Нет. UNSIGNED использует те же байты, что и signed тип. Размер INT всегда 4 байта, независимо от UNSIGNED.

Q: Можно ли использовать UNSIGNED в PostgreSQL?

Напрямую нет. Нужно использовать CHECK constraint: CHECK (column >= 0) или создать пользовательский домен.

Q: Что произойдёт при вставке -1 в UNSIGNED колонку?

В MySQL с strict mode — ошибка. Без strict mode — значение обрежется до 0 или произойдёт переполнение.

Q: Обязательно ли использовать UNSIGNED для AUTO_INCREMENT?

Не обязательно, но рекомендуется. Это удваивает количество возможных ID и семантически корректно.


Источники