Как изменить тип данных в SQL
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Базовый синтаксис
Postgres
ALTER TABLE users ALTER COLUMN age TYPE BIGINT;MySQL
ALTER TABLE users MODIFY COLUMN age BIGINT;1. С cast (USING)
Если автоматическое преобразование невозможно:
-- Postgres: строка в число
ALTER TABLE users
ALTER COLUMN age TYPE INT USING age::INT;
-- с обработкой невалидных
ALTER TABLE users
ALTER COLUMN age TYPE INT USING NULLIF(age, '')::INT;2. VARCHAR → TEXT
Обычно совместимо:
ALTER TABLE users ALTER COLUMN description TYPE TEXT;3. INT → BIGINT
Совместимо:
ALTER TABLE users ALTER COLUMN id TYPE BIGINT;4. FLOAT → NUMERIC
Может потерять точность. Обычно безопасно:
ALTER TABLE sales
ALTER COLUMN amount TYPE NUMERIC(10, 2) USING amount::NUMERIC(10, 2);5. TIMESTAMP без зоны → с зоной
-- Postgres
ALTER TABLE events
ALTER COLUMN created_at TYPE TIMESTAMPTZ
USING created_at AT TIME ZONE 'UTC';6. TEXT → JSON / JSONB
ALTER TABLE events
ALTER COLUMN payload TYPE JSONB USING payload::JSONB;Если в данных есть невалидный JSON — запрос упадёт. Сначала проверьте.
7. Уменьшение размера (опасно)
-- если в данных есть строки длиннее 50 — ошибка
ALTER TABLE users ALTER COLUMN bio TYPE VARCHAR(50);Проверьте до:
SELECT MAX(LENGTH(bio)) FROM users;8. Safe migration (без блокировки prod)
Для больших таблиц ALTER COLUMN TYPE может блокировать. Шаблон:
-- 1. Добавить новую колонку
ALTER TABLE users ADD COLUMN age_new BIGINT;
-- 2. Заполнить батчами (в приложении или через скрипт)
UPDATE users SET age_new = age::BIGINT WHERE age_new IS NULL
LIMIT 10000;
-- 3. Создать триггер для синхронизации новых записей
CREATE TRIGGER sync_age BEFORE INSERT OR UPDATE ON users
FOR EACH ROW EXECUTE PROCEDURE update_age_new();
-- 4. Проверить, что всё заполнено
SELECT COUNT(*) FROM users WHERE age_new IS NULL;
-- 5. Переименовать
ALTER TABLE users DROP COLUMN age;
ALTER TABLE users RENAME COLUMN age_new TO age;Для мелких таблиц — просто ALTER COLUMN TYPE.
9. DATE ↔ STRING
-- string → date
ALTER TABLE events
ALTER COLUMN created_at TYPE DATE
USING TO_DATE(created_at, 'YYYY-MM-DD');
-- date → string
ALTER TABLE events
ALTER COLUMN date_str TYPE VARCHAR(10)
USING TO_CHAR(date_col, 'YYYY-MM-DD');10. BOOLEAN ↔ INT
-- int → bool
ALTER TABLE users
ALTER COLUMN is_premium TYPE BOOLEAN
USING (is_premium::INT = 1);
-- bool → int
ALTER TABLE users
ALTER COLUMN flag_col TYPE INT
USING (flag_col::INT);CAST vs ALTER COLUMN
| Задача | Инструмент |
|---|---|
| Постоянно изменить тип в таблице | ALTER TABLE ... TYPE |
| Временно преобразовать в запросе | CAST / :: |
CAST в запросе:
SELECT age::INT FROM users;
SELECT CAST(age AS INT) FROM users; -- SQL стандартЧасто просматривают
- NUMERIC → INT (потеря дробной части):
(amount::INT) - INT → NUMERIC (безопасно)
- VARCHAR → INT (нужен USING)
- TEXT → DATE (нужен TO_DATE)
Частые ошибки
Нежизнеспособные значения
-- упадёт, если в age есть '30 лет' (не число)
ALTER TABLE users ALTER COLUMN age TYPE INT USING age::INT;Сначала:
SELECT * FROM users WHERE age !~ '^\d+$'; -- регексп на число
-- почистить или исключитьПотеря точности
FLOAT → INT теряет дробь. NUMERIC(10, 2) с числом 1234567.89 — не помещается (2 знака дроби + 8 целых > 10).
Блокировка prod-таблицы
ALTER COLUMN TYPE на большой таблице блокирует запись. Делайте в техокно или через shadow-колонку.
Несоответствие в коде
Код ожидает INT, а колонка стала STRING — запросы падают. Обновите код синхронно.
Связанные темы
FAQ
Можно ли изменить тип без потери данных?
Если типы совместимы (VARCHAR → TEXT, INT → BIGINT) — да. С преобразованием (VARCHAR → INT) — зависит от данных.
Блокирует ли ALTER COLUMN TYPE?
Postgres: для некоторых преобразований — да (нужна перезапись). Для совместимых — нет.
CAST или ::?
:: — синтаксис Postgres, короче. CAST(... AS ...) — стандарт SQL, работает везде.
Что делать, если ALTER падает на prod?
Shadow-колонка + пакетная миграция + rename. Подробнее в разделе «Safe migration».
Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.