Найти в Дзене

PostgreSQL: Меняем местами значения полей в таблице. Ночной код под неоновым светом

*04:23. Монитор мигает синим, за окном дрон-курьер ругается с беспилотной шаурмечной, а у меня курсовая по базам данных горит синим пламенем. Препод сказал: «Данные в таблице — как нейросети в головах у студентов. Если перепутал местами — переучивай, пока не починишь». Короче, надо поменять местами имена и фамилии в таблице. Погнали.* В нашем мире у каждого второй — с нейроинтерфейсом, но базы данных остались неприкосновенными. Тут вам не хакнуть корпорацию, тут надо просто, чтобы в колонке first_name лежало имя, а в last_name — фамилия. А у меня при загрузке данных из старого терминала всё перепуталось. Что имеем:
Таблица users, где Иван Петров почему-то оказался разложен не по полочкам: Иван в фамилиях, Петров в именах. Знакомая боль? Самый прямолинейный способ, который приходит в голову любому новичку: создать промежуточное хранилище, как запасной накопитель перед перепрошивкой. -- Шаг 1. Создаем временный столбец ALTER TABLE users ADD COLUMN temp_name VARCHAR(50); -- Проверяем, ч
Оглавление

*04:23. Монитор мигает синим, за окном дрон-курьер ругается с беспилотной шаурмечной, а у меня курсовая по базам данных горит синим пламенем. Препод сказал: «Данные в таблице — как нейросети в головах у студентов. Если перепутал местами — переучивай, пока не починишь». Короче, надо поменять местами имена и фамилии в таблице. Погнали.*

В нашем мире у каждого второй — с нейроинтерфейсом, но базы данных остались неприкосновенными. Тут вам не хакнуть корпорацию, тут надо просто, чтобы в колонке first_name лежало имя, а в last_name — фамилия. А у меня при загрузке данных из старого терминала всё перепуталось.

Что имеем:
Таблица users, где Иван Петров почему-то оказался разложен не по полочкам: Иван в фамилиях, Петров в именах. Знакомая боль?

Уровень 1: Простое решение. Через временный столбец

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

-- Шаг 1. Создаем временный столбец

ALTER TABLE users ADD COLUMN temp_name VARCHAR(50);

-- Проверяем, что колонка создалась

SELECT * FROM users LIMIT 1;

-- Шаг 2. Копируем имена в "буфер обмена"

UPDATE users SET temp_name = first_name;

-- Контрольный SELECT: данные скопировались?

SELECT id, first_name, temp_name FROM users LIMIT 5;

-- Шаг 3. Переносим фамилии на место имен

UPDATE users SET first_name = last_name;

-- Проверяем промежуточный результат

SELECT id, first_name, last_name FROM users LIMIT 5;

-- Шаг 4. Возвращаем имена из временного хранилища на место фамилий

UPDATE users SET last_name = temp_name;

-- Финальная проверка

SELECT id, first_name, last_name FROM users LIMIT 5;

-- Шаг 5. Удаляем временный столбец

ALTER TABLE users DROP COLUMN temp_name;

-- Убеждаемся, что структура вернулась к исходной

\d users

Способ работает. Но пока я писал эти пять запросов с проверками, допил второй стакан кофе и задумался о смысле жизни. Неужели в PostgreSQL нет ничего элегантнее?

Уровень 2: Оптимальное решение. Прямой обмен

А потом я вспомнил, как устроены транзакции в PostgreSQL. Сервер же не тупо построчно выполняет команды — он сначала читает исходные значения, а потом применяет изменения. Это значит, мы можем обменять поля одной строчкой.

Подготовка: проверяем, что поля совместимы

-- Проверяем типы данных

SELECT
data_type
FROM
information_schema.columns
WHERE
table_name = 'users'
AND column_name IN ('first_name', 'last_name');

-- Смотрим на данные: нет ли там NULL, которые могут дать неожиданный эффект?

SELECT
COUNT(*) FILTER (WHERE first_name IS NULL) as null_first,
COUNT(*) FILTER (WHERE last_name IS NULL) as null_last
FROM users;

-- Оцениваем масштаб бедствия

SELECT COUNT(*) FROM users;

Если всё чисто — делаем магию:м

UPDATE users
SET
first_name = last_name,
last_name = first_name;

Проверяем результат:

-- Смотрим, что получилось

SELECT id, first_name, last_name FROM users ORDER BY id;

Всё встало на свои места. Никаких временных таблиц, никакого оверхеда. Сервер сам разобрался, что к чему.

Уровень 3: Сложный случай. Когда типы разные

Но, как в любом хорошем киберпанк-романе, наступает момент, когда приходит легаси-код и говорит: «У меня тут text и integer перепутались, почини, но ничего не сломай».

Исходная ситуация: в колонке name (TEXT) лежат числа, а в колонке code (INTEGER) — имена. Да, бывает и такое.

Диагностика:

-- Смотрим структуру

SELECT
column_name,
data_type
FROM
information_schema.columns
WHERE
table_name = 'products';

-- Проверяем, можно ли name преобразовать в INTEGER

SELECT
COUNT(*) as total,
COUNT(*) FILTER (WHERE name ~ '^[0-9]+$') as convertible_to_int
FROM products;

Если не все строки преобразуются — нужно чистить данные.

Рефакторинг с нуля:

-- 1. Создаем новые колонки с правильными типами

ALTER TABLE products ADD COLUMN new_name TEXT;
ALTER TABLE products ADD COLUMN new_code INTEGER;

-- 2. Подготавливаем данные: оставляем в поле name только цифры

UPDATE products
SET name = REGEXP_REPLACE(name, '[^0-9]', '', 'g')
WHERE name ~ '[^0-9]';

-- Проверяем, что мусора не осталось

SELECT name FROM products WHERE name !~ '^[0-9]*$';

-- 3. Переносим данные с конвертацией

UPDATE products SET new_name = code::TEXT;
UPDATE products SET new_code = name::INTEGER;

-- 4. Проверяем, что данные перенеслись корректно

SELECT name, new_code, code, new_name FROM products LIMIT 10;

-- 5. Удаляем старые колонки

ALTER TABLE products DROP COLUMN name, DROP COLUMN code;

-- 6. Переименовываем новые

ALTER TABLE products RENAME COLUMN new_name TO name;
ALTER TABLE products RENAME COLUMN new_code TO code;

-- Финальная проверка

SELECT * FROM products LIMIT 10;

Итоги ночного кодера

Три способа — три уровня погружения в PostgreSQL. Для себя я вынес простую истину: не надо изобретать велосипед там, где база данных уже всё умеет.

Если типы совпадают — делаем простой UPDATE с обменом полей. Это быстро, чисто и профессионально.

Если типы разные — готовимся к рефакторингу, но всегда проверяем данные до и после. Одна ошибка в конвертации может убить прод быстрее, чем любой вирус.

05:30. Глаза слипаются. Курсовая готова. Препод, если ты это читаешь — данные теперь на своих местах, проверки выполнены. А я пошел спать.