Переход с SQL Server на PostgreSQL: несовместимости и план
Переход с SQL Server на PostgreSQL: ключевые несовместимости (типы данных, T-SQL, индексы), план миграции схемы и данных, базовые нагрузочные тесты.

Что именно усложняет переход
Компании переходят с SQL Server на PostgreSQL по разным причинам: лицензии и стоимость владения, требования к импортозамещению, желание уйти от привязки к одному вендору, изменения инфраструктуры (облако, контейнеры, смена команды поддержки).
Но миграция почти никогда не бывает переносом 1 к 1. Даже если «таблицы те же», различаются типы данных, поведение функций и сравнения строк, транзакции, правила блокировок и планы выполнения. Поэтому такой переход чаще оказывается проектом по адаптации приложения и процессов, а не только переносом схемы.
Обычно ломается то, что годами считалось «само собой разумеющимся»: запросы на T-SQL, хранимые процедуры и триггеры, динамический SQL, а также индексы, которые в одной СУБД помогали, а в другой дают неожиданные планы. Отдельный источник сюрпризов - даты и время, GUID, money/decimal, NVARCHAR и коллации, поведение NULL.
Перед стартом полезно быстро оценить риск по простому чеклисту:
- Сколько логики живет в базе (процедуры, триггеры, SQL-агенты, джобы).
- Насколько приложение зависит от T-SQL и специфичных функций.
- Объем данных и допустимое окно простоя.
- Критичные отчеты и «тяжелые» запросы, важные по времени ответа.
- Требования к отказоустойчивости, бэкапам и восстановлению.
В организациях с жесткими требованиями к поддержке и прозрачности поставок (госструктуры, финсектор) риск часто не в «переносе таблиц», а в том, чтобы после переключения все регламенты, отчеты и ночные загрузки работали предсказуемо.
Инвентаризация: что переносим и кто зависит от базы
Перед планированием миграции важно понять, что именно вы переносите. Часто «одна база» на деле включает несколько баз, набор схем, историю за годы и десятки интеграций, о которых вспоминают в последний момент.
Начните с простого паспорта текущей системы: список баз и схем, ключевые таблицы и их размер, темпы роста, критичные отчеты и витрины. Отдельно отметьте объекты, которые сложно пересоздать: сложные представления, функции, процедуры, триггеры.
Дальше зафиксируйте зависимости. Помимо основного приложения это обычно ETL-загрузки, BI-отчеты, сервисы обмена (очереди, API), фоновые задания, выгрузки в файлы, интеграции с бухгалтерией и мониторинг. На каждую зависимость стоит назначить владельца, чтобы правки согласовывались быстро.
Чтобы не промахнуться с производительностью, заранее выберите «профиль нагрузки»: какие операции читают больше всего, а какие пишут. На практике 10-20 запросов и 3-5 сценариев записи дают основную картину.
Минимум, который стоит собрать в один документ:
- объекты и объемы данных (с ростом за месяц)
- критичные отчеты и SLA по времени ответа
- список зависимых систем и контактных лиц
- топ запросов и сценариев записи по нагрузке
- допустимый простой и окна обслуживания
Пример: в производственной компании отчеты закрывают смену, и простой возможен только ночью. Это сразу задает требования к плану миграции, откату и времени прогрева кешей после переключения.
Несовместимости типов данных: где чаще всего ошибки
Самые неприятные баги обычно не про «не тот тип», а про тихую смену поведения: округления, сравнения дат, сортировку строк, автонумерацию. Снаружи схема выглядит похожей, а в продакшене внезапно «не сходятся» отчеты или ломаются уникальные ключи.
С числами все просто до первого «денежного» поля. INT и BIGINT обычно переносятся напрямую, а вот MONEY лучше не переносить как есть. В PostgreSQL нет полного аналога, и безопаснее перейти на NUMERIC(precision, scale) с явной точностью, иначе возможны неожиданные округления в суммах и налогах.
Идентификаторы тоже любят сюрпризы. UNIQUEIDENTIFIER почти всегда становится UUID. Проверьте, где UUID генерируется: в SQL Server это мог делать код приложения или default, а в PostgreSQL генерацию лучше задать явно и унифицировать формат.
С датой и временем ошибок больше всего. DATETIME2 обычно переносится в timestamp, но нужно выбрать: «без часового пояса» или timestamptz. Если часть системы считает время локальным, а часть как UTC, то после миграции сравнения и группировки по дате начнут давать другие результаты, особенно около смены дня.
Со строками главная ловушка - сортировка и регистр. NVARCHAR в PostgreSQL не нужен отдельно: Unicode поддерживается «из коробки». Но collation и правила сравнения отличаются, поэтому запрос «найти все, что начинается на А» может вернуть другой набор, чем на SQL Server.
Для бинарных данных VARBINARY обычно становится bytea. Если использовался FILESTREAM, чаще всего проще вынести файлы в объектное хранилище или файловый сервер, а в БД оставить метаданные и ссылки.
Отдельная тема - IDENTITY против последовательностей. В PostgreSQL это GENERATED ... AS IDENTITY или SEQUENCE, и важно проверить поведение при ручных вставках: можно легко «обогнать» счетчик и получить конфликт ключей.
Быстрая мини-проверка перед переносом таблицы:
- Есть ли
MONEYи задана ли точностьNUMERIC. - Есть ли GUID и где он генерируется.
- Какие поля времени должны быть в UTC, а какие нет.
- Есть ли зависимость от сортировки/регистра в поиске.
- Как устроена автонумерация и возможны ли ручные вставки.
Простой пример: в базе учета заявок поле created_at в SQL Server хранилось как локальное время, а отчеты строились «за сутки». После переноса в timestamptz без явного правила преобразования часть заявок попадет на соседнюю дату, и суточные итоги перестанут совпадать.
Поведение SQL и ограничения: нюансы, которые всплывают в проде
Сюрпризы часто не в типах данных, а в том, как SQL ведет себя в реальных запросах и транзакциях. Эти отличия обычно проявляются уже после релиза, когда данные становятся «грязнее», а нагрузка выше.
Частая причина скрытых багов - NULL и пустые строки. В SQL Server приложение иногда годами живет с допущением, что «пусто» это "". В PostgreSQL NULL и "" всегда разные, и условия вроде = '', <> '', LIKE '%' или конкатенация строк могут дать другой результат. Лучше заранее договориться, что считается «нет значения», и фиксировать это в коде (например, через COALESCE) и в ограничениях.
Вторая зона риска - кавычки и регистр имен. В SQL Server многие привыкли, что имена объектов работают без кавычек и почти всегда без учета регистра (зависит от collation). В PostgreSQL некавыченные имена приводятся к нижнему регистру, а имена в двойных кавычках становятся чувствительными к регистру. Если в схеме есть "CamelCase"-таблицы или столбцы, это быстро превращается в цепочку ошибок «не найдено».
Ограничения тоже стоит перепроверять на реальных сценариях. CHECK и FOREIGN KEY в PostgreSQL могут быть отложенными (DEFERRABLE) и проверяться в конце транзакции, что помогает при сложной загрузке данных. При этом каскады (ON DELETE CASCADE, ON UPDATE) и правила RESTRICT/NO ACTION лучше сверить отдельно, иначе можно получить неожиданное удаление цепочек данных или, наоборот, блокировку бизнес-операции.
Отдельно проверьте транзакции и блокировки. PostgreSQL использует MVCC, и ожидания по «кто кого блокирует» могут отличаться от привычных в SQL Server. То, что раньше проходило без задержек, может начать упираться в долгие транзакции и очереди ожидания.
Перед продакшеном полезно прогнать короткую проверку:
- найти места, где сравнивают с "" и где допускают NULL в логике
- собрать список объектов с кавычками и смешанным регистром
- сверить каскады и поведение FK на удалении/обновлении
- проверить типовые конкурирующие операции (обновление одной записи из двух потоков)
- оценить массовые UPDATE/DELETE на больших таблицах (время, блокировки, «раздувание» таблиц)
Большинство проблем проявляется не на тестовой выборке, а на живых данных и параллельных запросах.
T-SQL в запросах: что переписывать в первую очередь
Быстрее всего всплывают отличия именно в прикладных запросах. Начните с тех, что выполняются чаще всего: списки, поиск, отчеты, операции записи.
Самая частая правка - ограничение строк. В SQL Server это TOP (N) или OFFSET ... FETCH. В PostgreSQL для лимита используют LIMIT, а для пропуска - OFFSET. И почти всегда нужен ORDER BY: без него результат может «прыгать».
Типовые замены, которые дают максимум эффекта в первые дни:
SELECT TOP (10) ... ORDER BY ...->SELECT ... ORDER BY ... LIMIT 10OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY->OFFSET 20 LIMIT 10- Конкатенация строк:
a + b->a || b(и продумайте поведение при NULL) ISNULL(x, y)->COALESCE(x, y)(тип результата может отличаться)IIF(cond, a, b)->CASE WHEN cond THEN a ELSE b END
Сложнее обычно с «умной» записью. В SQL Server часто используется MERGE. В PostgreSQL чаще применяют UPSERT через INSERT ... ON CONFLICT (...) DO UPDATE. Важно, чтобы конфликт опирался на уникальный индекс или ограничение.
Функции дат и строк почти всегда требуют ручной проверки результата. Примеры: GETDATE() -> now(), DATEADD(day, 7, dt) -> dt + interval '7 days', форматирование через to_char(...). Даже при наличии аналога формат и округление могут отличаться.
Отдельная зона риска - TRY_CONVERT и «мягкие» преобразования. В PostgreSQL неудачный CAST обычно выбрасывает ошибку, поэтому безопаснее писать через CASE (например, сначала проверять строку регулярным выражением) или нормализовать данные до миграции.
Оконные функции в целом похожи: ROW_NUMBER() OVER (...), SUM(...) OVER (...) работают привычно. Но если в SQL Server вы фильтровали по оконной функции в том же запросе, в PostgreSQL чаще нужен подзапрос: сначала посчитать окно, потом отфильтровать.
T-SQL в коде базы: процедуры, триггеры, динамический SQL
Сложнее всего мигрировать не таблицы, а код внутри базы: процедуры, триггеры, функции. В SQL Server часто используется «процедурный» стиль на T-SQL с большим количеством побочных эффектов. В PostgreSQL похожие задачи решают функциями и процедурами на PL/pgSQL, но синтаксис и привычные конструкции отличаются.
При переписывании начните с логики, которая влияет на деньги и данные: расчеты, списания, закрытие периодов, правила валидации. Рабочая тактика: сначала перенести «чистые» запросы (SELECT/INSERT/UPDATE), затем обвязку (обработка ошибок, транзакции, ветвления).
Триггеры и временные структуры
В PostgreSQL триггеры бывают BEFORE/AFTER и FOR EACH ROW/STATEMENT, и от этого реально зависит нагрузка. Если в SQL Server логика была в одном триггере «на все случаи», то в PostgreSQL иногда проще разделить ее на несколько триггеров и зафиксировать дизайн. Порядок срабатывания тоже стоит проверить заранее.
Временные таблицы и табличные переменные нужно пересмотреть. Вместо табличных переменных чаще используют временные таблицы (TEMP) или CTE. Иногда подходят обычные таблицы с явной очисткой в транзакции, если данные должны пережить несколько шагов.
Динамический SQL, безопасность и права
Динамический SQL переносится через EXECUTE, но важнее не синтаксис, а параметры и безопасность. Склейка строк из пользовательского ввода в PostgreSQL так же опасна, поэтому используйте параметризацию и безопасное формирование идентификаторов.
Перед запуском проверьте:
- где используется EXEC/sp_executesql и какие параметры туда попадают
- какие процедуры требуют прав владельца, а какие должны работать от имени вызывающего
- соответствие ролей: логины, схемы, GRANT, доступ к функциям и последовательностям
- места, где код зависит от контекста (CURRENT_USER, поиск объектов по схеме)
- планы выполнения для самых частых динамических запросов (они могут стать нестабильными)
Если в организации жесткие требования к доступам, лучше сразу зафиксировать целевую модель ролей и прогнать ее на тестовой среде до переноса данных.
Индексы и производительность: отличия, влияющие на скорость
В SQL Server часто опираются на кластерный индекс: данные на диске физически упорядочены по ключу, и это влияет на чтение. В PostgreSQL базовый индекс - btree, но «кластерности» по умолчанию нет: строки лежат в порядке записи, а индекс хранит ссылки. Команда CLUSTER существует, но это разовая операция, и порядок со временем снова «расползается».
Покрывающие индексы тоже отличаются. В SQL Server привычен INCLUDE. В PostgreSQL INCLUDE тоже есть, но часто важнее другое: не вытаскивать лишние колонки в SELECT и не превращать фильтр в вычисление.
Сильная сторона PostgreSQL - специализированные индексы. GIN полезен для массивов, JSONB и полнотекстового поиска. GiST подходит для геоданных и некоторых «похожих» поисков. BRIN выручает на очень больших таблицах, где данные примерно упорядочены по времени: индекс маленький, а эффект бывает заметным.
Почему после миграции запрос вдруг медленнее? Часто виноваты планы: другая статистика, другой оптимизатор, и он выбирает «не тот» путь. Например, отчет по заказам может перейти с точечного поиска на последовательное сканирование, если статистика устарела или селективность фильтра оценена неверно.
Что стоит держать под контролем после переноса:
- ANALYZE после массовых загрузок и заметных изменений данных.
- VACUUM (и настройка autovacuum), чтобы не копился bloat.
- Индексы под реальные фильтры и JOIN, а не «на всякий случай».
- Планы выполнения: проверяйте, что используются ожидаемые индексы.
- Массовые обновления: следите за ростом таблиц и индексов.
План миграции схемы: пошагово и без лишней боли
Схема (DDL) переносится проще, если заранее договориться о правилах. Частая проблема - хаос с именами (dbo, разные владельцы, регистр) и «скрытые» зависимости: внешние ключи, дефолты, функции.
Порядок работ, который обычно не подводит
Сначала зафиксируйте целевую структуру: какие схемы будут в PostgreSQL (например, вместо dbo - app), как называются таблицы, кто владелец, какие права нужны. Это экономит часы исправлений после первой загрузки.
Дальше идите по шагам:
- Перенесите DDL таблиц и приведите типы данных к аналогам (например, NVARCHAR -> TEXT/VARCHAR, DATETIME -> TIMESTAMP, BIT -> BOOLEAN). На этом этапе лучше временно не включать внешние ключи.
- Добавьте первичные ключи и уникальные ограничения. Затем подключайте внешние ключи, начиная с «родительских» таблиц, чтобы не упереться в порядок создания.
- Настройте генерацию ключей: IDENTITY из SQL Server обычно превращается в identity columns или sequence. Сразу выставьте стартовые значения, иначе новые записи столкнутся по ключам.
- Перенесите CHECK-ограничения и DEFAULT-значения, но перепроверьте выражения: то, что работало в T-SQL, может потребовать переписывания.
- Создайте индексы после того, как базовая схема поднялась без ошибок, и включите базовые настройки для первых тестов.
Маленький практический пример
Если в SQL Server многие таблицы живут в dbo и ссылаются друг на друга, не пытайтесь создать все внешние ключи сразу. Сначала создайте таблицы и PK, затем прогоните скрипт FK отдельно. Так проще понять, где именно несовместимость, и не утонуть в сотнях ошибок одновременно.
План миграции данных: загрузка, проверки и откат
Заранее решите, какой режим вам подходит: полный перенос с окном простоя, инкрементальный перенос (двойная запись или репликация) или комбинированный вариант, когда большую часть вы грузите заранее, а в конце догоняете изменения. На практике часто выигрывает подход с коротким финальным простоем: он проще в управлении и легче в откате.
Порядок загрузки важнее скорости. Если начать с «фактов», вы быстро упретесь во внешние ключи и получите грязные данные. Практичный порядок:
- справочники (организации, пользователи, статусы)
- основные сущности (заказы, счета, карты пациентов и т.п.)
- таблицы связей (many-to-many)
- исторические и архивные таблицы
- служебные очереди и временные данные (если они вообще нужны)
Проверки делайте на каждом шаге. Минимум: сравните количество строк, суммы по ключевым числовым полям и контрольные суммы (например, по хэшу строки) для нескольких крупных таблиц. Добавьте выборочные сверки: 50-100 «живых» документов, которые пользователи часто открывают.
Для больших таблиц планируйте батчи и параллельную загрузку, но с логированием прогресса и повторяемостью. Пример: таблицу транзакций на 200 млн строк грузите по диапазонам даты, фиксируя, какие диапазоны успешно завершились.
Откат должен быть реальным, а не «на бумаге». Держите SQL Server в режиме чтения/записи до точки принятия, сохраняйте журнал миграций, заранее определите критерии возврата (ошибки целостности, критичное падение скорости, невозможность закрыть день).
Пример сценария: перенос прикладной базы без экзотики
Представим обычную систему учета заявок: сотрудники создают обращения, руководители смотрят статусы, бухгалтерия и ИТ раз в неделю выгружают отчеты. База небольшая, но к ней привязаны привычные отчеты и ежедневные списки.
Ключевые таблицы можно свести к двум. tickets хранит идентификатор, дату создания, статус, подразделение и исполнителя. ticket_events хранит изменения по заявке: кто поменял статус и когда. Этого достаточно, чтобы увидеть типовые проблемы.
Критичны обычно не «сложные» запросы, а самые частые:
- список заявок по статусу с сортировкой по дате (для оператора)
- поиск по номеру или части темы (для колл-центра)
- фильтры по датам и подразделению (для руководителя)
- агрегаты для отчета: сколько закрыто за день/неделю, среднее время обработки
В этом примере быстро всплывают несовместимости. Даты и время: в SQL Server часто используют GETDATE() и неявные преобразования, в PostgreSQL лучше явно работать с CURRENT_TIMESTAMP и типами timestamp/date. GUID: uniqueidentifier обычно переезжает в uuid, но важно проверить формат ввода и генерацию идентификаторов. Пагинация: TOP и OFFSET/FETCH переписываются на LIMIT/OFFSET. UPSERT: вместо MERGE чаще используют INSERT ... ON CONFLICT.
-- Пагинация
SELECT ... ORDER BY created_at DESC LIMIT 50 OFFSET 100;
-- UPSERT
INSERT INTO tickets(id, status, updated_at)
VALUES ($1, $2, CURRENT_TIMESTAMP)
ON CONFLICT (id) DO UPDATE
SET status = EXCLUDED.status, updated_at = EXCLUDED.updated_at;
Чтобы миграция не была «на глаз», зафиксируйте базовую линию до переноса: 5-10 самых частых запросов, их среднее время и 95-й перцентиль, ожидаемое число строк. После переноса сравните две вещи: корректность (совпали ли суммы, количества, выборки за период) и скорость (время ответа при одинаковых фильтрах и объеме данных). Если хотя бы один показатель «поплыл», искать причину проще, когда есть конкретные цифры и примеры запросов.
Минимальные нагрузочные тесты перед запуском
Перед запуском важно не пытаться «проверить все», а подтвердить главное: система держит реальную нагрузку без ошибок и сюрпризов по времени ответа. Минимальный набор можно собрать за 1-2 дня, если брать реальные запросы и параметры.
Три теста, которые дают максимум сигнала
Обычно хватает трех прогонов, чтобы поймать большую часть проблем:
- Чтение с конкуренцией: возьмите 10 самых частых запросов из логов продакшена (или APM), подставьте типичные параметры и запустите параллельно (например, 10-50 одновременных пользователей). Смотрите не только среднее время, но и «хвост» медленных ответов.
- Запись и массовые изменения: прогон на вставки, обновления и upsert-паттерны, которые использует приложение. Обязательно включите параллельность и измеряйте блокировки: частая проблема после миграции - ожидание из-за транзакций и уровней изоляции.
- Смешанная нагрузка плюс отчеты: запустите фоновые отчеты и агрегаты на типичном диапазоне дат (например, «последние 30 дней»), одновременно имитируя обычных пользователей. Так быстрее всего всплывают слабые индексы и неудачные планы.
Чтобы тесты были честными, делайте их на копии данных, близкой по объему к боевой, и прогрейте кэш коротким прогоном перед измерениями.
Критерии приемки
Заранее зафиксируйте, что считается «годно», иначе результаты будут спорными:
- Время ответа: p95 и p99 для ключевых запросов не хуже согласованного порога.
- Ошибки: ноль ошибок приложения и драйвера (таймауты, deadlock, serialization failure) при целевой конкуренции.
- Блокировки: ожидания блокировок не растут с временем прогона.
- Рост диска и WAL: прогнозируемый рост, без резких скачков и переполнения.
- Стабильность: одинаковые результаты в 2-3 повторных прогонах.
Пример: если отчет «Продажи за месяц» в SQL Server открывался за 3-5 секунд, в PostgreSQL допустимы 4-6 секунд, но p99 не должен уезжать в минуты при параллельной записи. Такой перекос обычно указывает, что нужен индекс, переписанный запрос или другой подход к транзакциям.
Частые ошибки и ловушки при миграции
Самая неприятная часть - мелкие различия, которые тихо меняют поведение системы. Кажется, что все работает, пока не начинают расходиться отчеты, не появляются задержки или не ломаются права.
Одна из частых ловушек - сортировка и сравнение строк. В SQL Server многое завязано на collation базы или столбца, а в PostgreSQL - на locale/ICU и collation конкретного поля. В итоге отчет с фамилиями может начать сортировать «Ё» иначе, а поиск по строке стать чувствительным или нечувствительным к регистру не так, как раньше.
Вторая ловушка - попытка перенести индексы «как было». В PostgreSQL другой оптимизатор, а лишние индексы дорого стоят при записи и вакууме. После миграции вставки и обновления часто замедляются, а размер базы растет просто потому, что «привычный» набор индексов оказался лишним.
Отдельно проверьте логику в триггерах. Если в SQL Server триггер делал «немного магии» на вставке, в PostgreSQL это может превратиться в очередь блокировок и длинные транзакции, особенно при пакетной загрузке.
Права и роли ломаются не реже. Сервисы могут использовать один логин, а в PostgreSQL у него внезапно нет прав на схему, последовательности или функции. Ошибка выглядит как «permission denied», но причина обычно в неполной карте ролей.
Быстрые проверки перед первым запуском:
- сравните сортировку на 10-20 ключевых отчетах и справочниках
- пересоберите индексы под реальные запросы, а не по привычке
- измерьте время типовых операций записи (INSERT/UPDATE) на тестовых данных
- проверьте права сервисных учеток на схемы, последовательности и функции
- включите базовый мониторинг: медленные запросы, блокировки, рост таблиц
Чеклист и следующие шаги
Чтобы миграция прошла предсказуемо, полезно заранее зафиксировать, что должно быть готово до первой тестовой загрузки и до запуска в продакшен.
По схеме и данным проверьте:
- Инвентаризация: какие базы, схемы, таблицы, задания, процедуры, триггеры, отчеты и приложения завязаны на БД, кто владелец, как выглядит критичный путь.
- DDL и типы: сопоставление типов, правил NULL/DEFAULT, identity/sequence, колляций и чувствительности к регистру.
- Замена T-SQL: список самых частых запросов и «точек боли» (TOP/OFFSET, MERGE, обработка дат, TRY...CATCH), плюс план переписывания.
- Индексы и ограничения: какие индексы реально используются, где нужны составные/частичные, какие UNIQUE/FOREIGN KEY важны для качества данных.
- Проверки данных: сверки, контрольные суммы, выборочные выборки, правила очистки и сценарий работы с «грязными» значениями.
По запуску и эксплуатации:
- Корректность: ключевые отчеты и операции дают те же результаты на контрольных данных.
- Нагрузка: 3-5 типовых сценариев (поиск, запись, пакетная обработка) с реальными объемами и замером времени.
- Восстановление: тест резервного копирования и восстановление на отдельный стенд.
- План отката: критерий «стоп», кто принимает решение, как возвращаетесь на старую БД.
- Запуск: пилот на ограниченном трафике, затем расширение и полный перевод.
Отдельно назначьте владельца инфраструктуры: серверы, хранение, бэкапы, мониторинг и 24/7 поддержку. Если нужна помощь с целевой архитектурой и расчетом ресурсов под нагрузку, это обычно удобно обсуждать с командой системной интеграции GSE.kz, параллельно подбирая серверы под задачи (например, из линейки S200 Series).
FAQ
Почему миграция с SQL Server на PostgreSQL редко бывает «1 к 1»?
Чаще всего сложность не в таблицах, а в поведении: типы данных «похожи», но округления, сравнения строк, работа с датой/временем, NULL и планы выполнения отличаются. Поэтому обычно приходится адаптировать запросы, код в базе и операционные процедуры (задания, отчеты, бэкапы), а не только сделать экспорт/импорт.
Как быстро оценить риск миграции до старта проекта?
Начните с инвентаризации: сколько логики живет в базе (процедуры, триггеры, джобы), насколько приложение зависит от T-SQL, какой объем данных и какое окно простоя допустимо. Отдельно отметьте критичные отчеты и самые тяжелые запросы, потому что именно они чаще всего «плывут» по времени ответа после переключения.
Что обязательно включить в инвентаризацию перед переносом?
Соберите «паспорт» системы: список баз и схем, размеры ключевых таблиц, рост данных, критичные витрины/отчеты и SLA. Затем зафиксируйте зависимости: ETL, BI, интеграции, фоновые задания, выгрузки, сервисы обмена, и назначьте владельца на каждую зависимость, чтобы изменения согласовывались быстро.
Как правильно перенести поля MONEY и избежать ошибок в расчетах?
Не переносите MONEY «как есть». В PostgreSQL безопаснее сразу перейти на `NUMERIC(precision, scale)` с явной точностью и масштабом и перепроверить расчеты сумм, налогов и итогов на контрольных наборах данных. Это снижает риск тихих расхождений из‑за округлений.
Что учесть при переносе UNIQUEIDENTIFIER в UUID?
Обычно `UNIQUEIDENTIFIER` переносится в `UUID`, но критично договориться, где он генерируется: в приложении или в базе как значение по умолчанию. Проверьте формат входных данных, правила генерации для новых записей и то, что ключи действительно уникальны после загрузки.
Почему даты и время чаще всего ломают отчеты после миграции?
Сначала решите, какие поля должны храниться как локальное время, а какие как UTC, и зафиксируйте это правилом, а не привычкой. Затем выберите между `timestamp` и `timestamptz` и проверьте отчеты, которые группируют по дате, особенно на границах суток и при переходе на летнее/зимнее время.
Чем опасны NULL и пустые строки при переходе на PostgreSQL?
В PostgreSQL NULL и пустая строка всегда разные, поэтому условия вроде `= ''` и конкатенация строк могут дать другой результат, чем вы ожидали. Лучше заранее определить, что считается «нет значения», и привести логику к явному виду через `COALESCE` и корректные ограничения в таблицах.
Почему после переноса внезапно «не находятся» таблицы и колонки?
В PostgreSQL некавыченные имена приводятся к нижнему регистру, а имена в двойных кавычках становятся чувствительными к регистру. Если в схеме есть CamelCase-имена, проще заранее договориться о единых правилах именования и не использовать кавычки без необходимости, иначе вы получите цепочку ошибок «объект не найден».
Что переписывать в T-SQL в первую очередь, чтобы приложение ожило?
Начните с самых частых запросов: замените `TOP` на `LIMIT`, перепишите конкатенацию `+` на `||`, `ISNULL` на `COALESCE`, `IIF` на `CASE`. Для `MERGE` обычно используйте `INSERT ... ON CONFLICT DO UPDATE`, но убедитесь, что конфликт опирается на уникальный индекс или ограничение, иначе поведение будет непредсказуемым.
Какие нагрузочные тесты достаточно провести перед запуском в продакшен?
Сделайте минимальный набор: конкурентное чтение по реальным запросам, параллельную запись (вставки/обновления/upsert) и смешанный прогон с отчетами на типичном диапазоне дат. Приемку фиксируйте числами: p95/p99 по ключевым операциям, отсутствие ошибок драйвера и рост ожиданий блокировок, а также прогнозируемый рост диска и журнала изменений.