Миграция данных между схемами PostgreSQL без потерь
Перенос таблицы в другую схему может занять одну команду, а копирование данных — потребовать паузы в записи и проверки связей. Разбираем, какой способ выбрать в PostgreSQL 16 и как не получить две расходящиеся версии таблицы.

В PostgreSQL 16 перенести таблицу в другую схему можно командой ALTER TABLE ... SET SCHEMA — строки при этом не копируются. Если же создать новую таблицу и перелить в неё данные, придётся решить другую задачу: как остановить расхождение между источником и копией, пока приложение продолжает запись. Ошибка в выборе способа стоит времени на простой и может оставить сервис с неверными данными.
Сначала разделите два разных сценария
Схема в PostgreSQL — не отдельная база, а пространство имён внутри неё. Таблицы app.orders и reporting.orders — разные объекты, даже если названия совпадают. Поэтому «перенести между схемами» может означать две вещи: переместить существующую таблицу или скопировать её данные в новую.
- Перемещаете саму таблицу. Данные остаются на месте, а таблица получает другое имя схемы. Обычно это быстрее и проще.
- Создаёте новую таблицу. Данные нужно скопировать, проверить и переключить приложение. Источник и приёмник какое-то время могут различаться.
Не начинайте с копирования, если достаточно переместить объект. И наоборот: команда перемещения не создаст независимую копию для поэтапного переключения.
Три способа и их цена
| Способ | Когда подходит | Чем платите |
|---|---|---|
ALTER TABLE ... SET SCHEMA | Нужно изменить схему существующей таблицы в той же базе | Нужны права и краткое окно блокировки; вызовы со старым именем могут перестать работать |
| Копирование и переключение | Нужна новая таблица, а старую требуется сохранить для отката или сверки | Дополнительное место, время на копирование и пауза в записи либо механизм синхронизации |
| Постепенный перенос с синхронизацией | Простой недопустим, а таблица велика или активно меняется | Сложность: нужно надёжно догонять изменения и проверить момент переключения |
Если достаточно переместить таблицу
Для переноса внутри одной базы PostgreSQL используйте команду для конкретной таблицы. Целевая схема должна существовать, а в ней не должно быть таблицы с таким же именем:
BEGIN;
ALTER TABLE app.orders SET SCHEMA reporting;
COMMIT;В PostgreSQL 16 это перемещает сам объект, не переписывая строки. Связанные с таблицей индексы и принадлежащие ей последовательности перемещаются вместе с ней. Но команда не переносит в новую схему всё, что ссылается на таблицу: представления, функции и другие объекты нужно проверить отдельно.
Перед запуском найдите обращения вида app.orders в коде приложения, SQL-запросах, заданиях и конфигурации. Запросы без схемы зависят от search_path — списка схем, в которых PostgreSQL ищет объекты по имени. После переноса имя может разрешаться иначе или перестать находиться. Сверьте также права и настройки прав по умолчанию для целевой схемы: они не обязаны совпадать с прежними.
Быстрое изменение имени объекта не означает, что все потребители автоматически найдут его по новому адресу.
Команда изменения схемы требует блокировки таблицы. Пока транзакция держит блокировку, другие запросы к объекту могут ждать. Для большой системы согласуйте окно, проверьте активные транзакции и не запускайте перенос вслепую в час пик.
Если нужна новая таблица: копируйте с остановкой записи
Ниже — пример для короткой миграции таблицы app.orders с тремя столбцами. В реальном проекте заранее подготовьте целевую таблицу с нужными ограничениями, индексами, настройками генерации идентификаторов и правами. Схема столбцов в примере должна совпадать с вашей.
Этот вариант берите для окна обслуживания: сначала остановите запись приложения, затем выполните копирование и переключение в одной транзакции. Блокировка защищает от чтения и изменений исходной таблицы до завершения транзакции.
CREATE SCHEMA IF NOT EXISTS reporting;
CREATE TABLE reporting.orders (
id bigint PRIMARY KEY,
status text NOT NULL,
created_at timestamptz NOT NULL
);
BEGIN;
LOCK TABLE app.orders IN ACCESS EXCLUSIVE MODE;
INSERT INTO reporting.orders (id, status, created_at)
SELECT id, status, created_at
FROM app.orders;До переключения проверьте количество строк и содержимое в обе стороны. Запросы ниже должны вернуть ноль строк. Сверяйте все столбцы, которые должны совпасть, а не только идентификаторы:
SELECT count(*) FROM app.orders;
SELECT count(*) FROM reporting.orders;
SELECT id, status, created_at FROM app.orders
EXCEPT ALL
SELECT id, status, created_at FROM reporting.orders;
SELECT id, status, created_at FROM reporting.orders
EXCEPT ALL
SELECT id, status, created_at FROM app.orders;Если проверки прошли, а зависимые объекты проверены отдельно, можно переключить имя таблицы и завершить транзакцию:
ALTER TABLE app.orders RENAME TO orders_old;
ALTER TABLE reporting.orders SET SCHEMA app;
COMMIT;Пример годится только для случая, когда зависимости от старой таблицы не должны остаться привязанными к ней. Внешние ключи, представления и другие объекты могут продолжить ссылаться именно на старый объект, переименованный в orders_old. Проверьте их до переключения и перенесите или пересоздайте по плану. Не удаляйте старую таблицу, пока не подтвердили работу приложения и не решили, сколько времени она нужна для отката.
Не вставляйте в целевую таблицу автоматически сгенерированные идентификаторы без проверки типа столбца. Для столбца с идентичностью типа GENERATED ALWAYS явную вставку значений нужно оформлять по правилам PostgreSQL; после переноса также проверьте, что дальнейшая генерация идентификаторов не столкнётся с уже занятыми значениями.
Когда нужен перенос без долгой паузы
Для крупной или активно изменяемой таблицы копирование под эксклюзивной блокировкой может занять слишком долго. Тогда данные переносят по этапам: готовят целевую структуру, выполняют начальную загрузку, синхронизируют изменения, сверяют результат и только после этого переключают приложение.
Ключевое условие — не допустить потерю записей, изменённых после начала копирования. Для этого нужен согласованный механизм синхронизации: например, двойная запись в приложении или поток изменений. Само копирование через INSERT ... SELECT за такими изменениями не следит. Конкретный механизм зависит от архитектуры и объёма нагрузки; без него нельзя считать две таблицы синхронными.
Что чаще всего ломается
- Запись продолжается в источник. Копия быстро устаревает. Остановите запись на время переноса или настройте и проверьте синхронизацию изменений.
- В целевой схеме уже есть объект с таким именем. Перенос завершится ошибкой. Проверьте имя заранее и не удаляйте существующую таблицу автоматически.
- Не совпадают ограничения или данные. Вставка может завершиться ошибкой уникальности или обязательного значения. Сверьте структуру и проверьте результат до переключения.
- Приложение продолжает обращаться к старой схеме. Исправьте квалифицированные имена и проверьте настройки
search_pathво всех компонентах. - Забыты зависимости. Внешний ключ или представление может остаться связанным со старым объектом. Инвентаризируйте зависимости до переименования.
Как выбрать способ
- Если нужна та же таблица под новым адресом внутри базы — проверьте зависимости и рассмотрите
ALTER TABLE ... SET SCHEMA. - Если нужна самостоятельная копия и допустимо остановить запись — заранее подготовьте структуру, скопируйте данные в транзакции и выполните проверки до переключения.
- Если простоя почти не должно быть — спроектируйте синхронизацию изменений и отдельно проверьте процедуру переключения и отката. Не подменяйте её одной начальной копией.
Cutover — момент переключения приложения на новую таблицу или схему. Это отдельный этап миграции: данные могут быть скопированы, но приложение ещё не использует целевой объект.
Что сделать дальше
Сначала составьте список зависимостей от таблицы: запросы приложения, внешние ключи, представления, функции, задания и права. Затем прогоните план на копии базы с тем же объёмом данных. Во время проверки измерьте длительность блокировки, размер дополнительного места и время сверки; в рабочей системе заранее определите, кто останавливает запись и по каким признакам можно безопасно отменить переключение.
Проверка перед сдачей: Заимствования, вода, структура, соответствие требованиям
Спросить за 20 ₽Источники: PostgreSQL 16: ALTER TABLE, PostgreSQL 16: схемы
Частые вопросы
Переместятся ли строки при ALTER TABLE SET SCHEMA?
Можно ли копировать данные, пока приложение пишет в исходную таблицу?
Что делать, если у меня другая версия PostgreSQL?
Почему после переключения внешний ключ всё ещё связан со старой таблицей?
Как понять, что копия совпадает с источником?
Что если миграция не сработала после переключения?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.