2435 слов
12 мин.
Миграция с MSSQL на PostgreSQL: Часть 1. Теория

Источник изображения обложки: Source

Миграция данных и приложений с одной базы данных на другую — это задача, которая возникает довольно часто в процессе оптимизации работы IT-инфраструктуры. Сегодня мы поговорим о миграции с Microsoft SQL Server (MSSQL) на PostgreSQL, популярную открыто-исходную реляционную базу данных. Процесс миграции может быть сложным, но с правильным подходом его можно упростить и эффективно выполнить.

В этой статье мы рассмотрим основные этапы миграции и проблемы, с которыми вы можете столкнуться.

Зачем мигрировать с MSSQL на PostgreSQL?#

Перед тем как перейти к процессу миграции, важно понять, почему стоит это сделать. Рассмотрим, зачем компании принимают такое решение, основные причины, плюсы и минусы этого процесса.

Основные причины для миграции#

  1. Снижение затрат. PostgreSQL — это полностью бесплатная и открытая СУБД, распространяемая под лицензией PostgreSQL. В отличие от MSSQL, который требует оплаты лицензий (как на использование, так и на масштабирование), PostgreSQL позволяет значительно сократить затраты на эксплуатацию.
  2. Открытый исходный код. PostgreSQL — это open-source проект с большим сообществом разработчиков. Это даёт пользователям доступ к исходному коду, что открывает широкие возможности для кастомизации и интеграции с другими решениями.
  3. Кроссплатформенность. PostgreSQL работает на различных операционных системах, включая Linux, Windows и macOS. Это делает её более гибкой для компаний, которые используют разнородные технологические стеки.
  4. Широкий функционал. PostgreSQL предлагает мощные функции, такие как поддержка JSON/JSONB, сложные типы данных, расширяемость за счёт пользовательских функций и модулей, а также надёжные механизмы репликации и масштабирования.
  5. Соответствие стандартам. PostgreSQL является одной из самых стандартизированных СУБД (SQL:2011). Это упрощает миграцию и делает базу данных более универсальной для работы с различными системами.

Плюсы PostgreSQL#

КритерийОписание
Экономическая выгодаОтсутствие затрат на лицензирование.
Бесплатные обновления и поддержка сообщества.
ГибкостьПоддержка различных форматов данных.
Возможность создания пользовательских расширений.
Высокая производительностьОтлично справляется с большими объёмами данных.
Эффективная работа с параллельными запросами.
БезопасностьРасширенные механизмы аутентификации и шифрования.
Поддержка управления доступом на уровне строк (Row-Level Security).
Активное сообществоБыстрая реакция на уязвимости.
Большое количество готовых решений и документации.

Минусы PostgreSQL#

КритерийОписание
Кривая обученияДля специалистов, привыкших к MSSQL, потребуется время на изучение особенностей PostgreSQL
Миграция данныхПроцесс переноса базы данных может быть трудоёмким, особенно для сложных схем с использованием специфичных функций MSSQL.
Отсутствие некоторых функцийMSSQL имеет встроенные инструменты, такие как SSRS (SQL Server Reporting Services) и SSAS (SQL Server Analysis Services), которые нужно заменить сторонними решениями при переходе.
Поддержка крупных вендоровPostgreSQL не предлагает коммерческую поддержку “из коробки”, как это делает Microsoft, хотя есть компании, предоставляющие такие услуги.

Кому подойдёт миграция?#

Переход с MSSQL на PostgreSQL особенно актуален для:

  • Стартапов и малых бизнесов, которые хотят минимизировать затраты на инфраструктуру.
  • Компаний, стремящихся к независимости от крупных вендоров.
  • Организаций, работающих с большими объёмами данных или сложными типами данных.
  • IT-команд, готовых инвестировать время в настройку и оптимизацию новой СУБД.

Этапы миграции#

Этап 1. Анализ текущей базы данных#

Перед началом миграции необходимо:

  • Провести инвентаризацию всех объектов базы данных, включая таблицы, индексы, хранимые процедуры и триггеры.
  • Оцените, сколько данных необходимо перенести, и подготовьтесь к возможным ограничениям производительности.
  • Проверить совместимость типов данных и функций.
Сложные элементы

Хранимые процедуры, функции и специфичные для MSSQL конструкции требуют ручной доработки.

Этап 2. Подготовка к миграции#

  • Установите PostgreSQL и настройте сервер в соответствии с требованиями проекта.
  • Выберите инструмент для миграции данных (например, pgloader, ora2pg, или пользовательские скрипты).
  • Разработайте план миграции, учитывающий зависимости между объектами базы данных.

Этап 3. Создание схемы базы данных в PostgreSQL#

  • Переведите структуру таблиц из MSSQL в PostgreSQL, учитывая различия в синтаксисе (описаны ниже).
  • Адаптируйте индексы, ограничения и связи.

Этап 4. Перенос данных#

  • Экспортируйте данные из MSSQL в формат, совместимый с PostgreSQL (например, CSV).
  • Загрузите данные в PostgreSQL, используя утилиты вроде COPY или инструменты миграции.
  • Убедитесь, что данные корректно перенесены, проведя выборочную проверку.

Этап 5. Адаптация SQL-запросов и бизнес-логики#

  • Измените хранимые процедуры, триггеры и представления для работы с PostgreSQL.
  • Перепишите специфичные запросы с учетом различий в синтаксисе.

Этап 6. Тестирование и оптимизация#

  • Проведите функциональное и нагрузочное тестирование на новой базе данных.
  • Оптимизируйте запросы и настройте параметры PostgreSQL для достижения высокой производительности.

Этап 7. Переход на новую базу данных#

  • Перенаправьте приложение на использование PostgreSQL.
  • Оставьте резервную копию MSSQL на случай необходимости отката.

Различия в синтаксисе MSSQL и PostgreSQL#

Типы данных#

NVARCHAR, VARCHAR(MAX), TEXT#

MSSQLPostgreSQL
NVARCHAR, VARCHAR(MAX), TEXTTEXT или VARCHAR

Основное отличие заключается в том, что PostgreSQL более универсально работает с TEXT, который не имеет ограничений по длине, в то время как в MSSQL для этого используется VARCHAR(MAX). NVARCHAR в MSSQL предназначен для хранения Unicode-данных, но в PostgreSQL поддержка Unicode реализована по умолчанию для всех строковых типов, поэтому нет необходимости использовать отдельный тип для этого.

DATETIME#

MSSQLPostgreSQL
DATETIMETIMESTAMP

В MSSQL тип данных DATETIME используется для хранения даты и времени. Однако в PostgreSQL аналогом для этого типа является TIMESTAMP. Основное отличие между ними состоит в уровне детализации и поддержке временных зон. В PostgreSQL есть два варианта TIMESTAMP:

  • TIMESTAMP WITHOUT TIME ZONE: используется для хранения даты и времени без учета часового пояса.
  • TIMESTAMP WITH TIME ZONE: позволяет учитывать часовые пояса, что может быть полезно в международных приложениях.
Внимание

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

MONEY#

MSSQLPostgreSQL
MONEYNUMERIC

В MSSQL тип данных MONEY используется для хранения денежных значений. Однако, в PostgreSQL его аналогом является NUMERIC, поскольку он обеспечивает более гибкое и точное представление чисел с плавающей точкой.

Почему не MONEY в PostgreSQL?

В PostgreSQL тоже есть тип MONEY, но его использование не рекомендуется, так как он менее универсален и сложнее в обработке для математических операций, чем NUMERIC.

NUMERIC позволяет точно задавать количество цифр до и после запятой, что полезно для финансовых операций, где критична точность.

Пример конверсии:

  • MSSQL:
CREATE TABLE transactions (
    amount MONEY
);
  • PostgreSQL:
CREATE TABLE transactions (
    amount NUMERIC(15, 2)  -- 15 цифр, из которых 2 после запятой
);

UNIQUEIDENTIFIER#

MSSQLPostgreSQL
UNIQUEIDENTIFIERUUID

В MSSQL тип данных UNIQUEIDENTIFIER используется для хранения глобально уникальных идентификаторов (GUID). Его аналог в PostgreSQL — тип UUID, который также предназначен для работы с уникальными идентификаторами.

Ключевые моменты

Формат: Оба типа представляют GUID в текстовом формате, например: 550e8400-e29b-41d4-a716-446655440000.

Генерация значений: - В MSSQL можно использовать функцию NEWID() для генерации GUID. - В PostgreSQL есть функция gen_random_uuid() (при наличии расширения pgcrypto) для генерации UUID.

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

BIT#

MSSQLPostgreSQL
BITBOOLEAN

В MSSQL тип данных BIT используется для представления логических значений, где 0 означает FALSE, а 1 — TRUE. В PostgreSQL для этого используется тип данных BOOLEAN, который является более естественным для работы с булевыми значениями.

Особенности

В PostgreSQL значения BOOLEAN представляются словами: TRUE, FALSE и NULL (если отсутствует значение).

При миграции данные типа BIT автоматически преобразуются в BOOLEAN, но в SQL-запросах стоит заменить числа 0 и 1 на соответствующие булевы значения.

Пример конверсии:

  • MSSQL:
CREATE TABLE example (
    is_active BIT
);
INSERT INTO example (is_active) VALUES (1), (0);
  • PostgreSQL:
CREATE TABLE example (
    is_active BOOLEAN
);
INSERT INTO example (is_active) VALUES (TRUE), (FALSE);

Сопоставление типов#

Чтобы не растягивать статью, ниже приведу таблицу сопоставления типов данных.

MSSQLPostgreSQL
BIGINTBIGINT, INT8
BINARY(n)BYTEA
VARBINARY(n)BYTEA
VARBINARY(max)BYTEA
ROWVERSIONBYTEA
IMAGEBYTEA
FIELDHIERARCHYIDBYTEA, LTREE (расширение)
BITBOOLEAN, BOOL
CHAR(n), n<=8000TEXT
NCHAR(n), n<=4000TEXT
VARCHAR(n), n<=8000TEXT
NVARCHAR(n), n<=4000TEXT
VARCHAR(max)TEXT
NVARCHAR(max)TEXT
TEXTTEXT
NTEXTTEXT
FLOAT(n)DOUBLE PRECISION, FLOAT8
SMALLMONEYNUMERIC, MONEY
MONEYNUMERIC, MONEY
INT, INTEGERINT, INTEGER, INT4
SMALLINTSMALLINT, INT2
NUMERIC(n,m)NUMERIC(n,m)
DEC(n,m), DECIMAL(n,m)DEC(n,m), DECIMAL(n,m)
TINYINTSMALLINT, INT2
REALREAL, FLOAT4
UNIQUEIDENTIFIERUUID, CHAR(16)
DATEDATE
TIME(n)TIME(n)
DATETIMETIMESTAMP(3)
DATETIME2(n)TIMESTAMP(m)
DATETIMEOFFSET(n)TIMESTAMP(n) WITH TIME ZONE, TIMESTAMPTZ
SMALLDATETIMETIMESTAMP(0)
XMLXML
Примечание

Типы CHAR и VARCHAR лучше не использовать. Причины подробно описаны здесь.

Более подробно о типах данных

Работа с идентификаторами#

В MSSQL идентификаторы оборачиваются в квадратные скобки [column_name]. Это полезно, если имя содержит пробелы или специальные символы. В PostgreSQL вместо этого используются двойные кавычки: "column_name". PostgreSQL чувствителен к регистру идентификаторов, если они заключены в кавычки.

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

SELECT "Column_Name" FROM table_name;

В этом случае "Column_Name" и column_name будут считаться разными именами.

Практическое значение

При миграции из MSSQL нужно убедиться, что имена объектов корректно адаптированы к правилам PostgreSQL. Если в MSSQL использовались скобки, их необходимо заменить на кавычки.

Также важно унифицировать регистр имен (обычно используются строчные буквы в PostgreSQL) во избежание путаницы.

Автоинкремент#

Рассмотрим как в MSSQL и PostgreSQL реализуется автоматическая генерация значений для первичных ключей или других колонок, которые требуют последовательного увеличения (автоинкремент).

  • В MSSQL для автоинкремента используется IDENTITY:
CREATE TABLE example (
    id INT IDENTITY(1,1), -- '1,1' означает, что автоинкремент начинается с 1 и увеличивается на 1
    name NVARCHAR(100)
);
Особенности

Значение автоматически генерируется при вставке новых записей.

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

  • В PostgreSQL используется SERIAL или GENERATED:
    1. SERIAL — удобный синтаксический сахар для автоматической генерации последовательности.
    2. GENERATED ALWAYS AS IDENTITY — современный стандарт SQL.

Пример с SERIAL:

CREATE TABLE example (
    id SERIAL, -- создается последовательность, связанная с колонкой
    name VARCHAR(100)
);

Пример с GENERATED:

CREATE TABLE example (
    id INT GENERATED ALWAYS AS IDENTITY,
    name VARCHAR(100)
);

Различия:

  • Гибкость:

    • В PostgreSQL последовательности, созданные с SERIAL, можно явно изменять или использовать в других контекстах.
    • IDENTITY в MSSQL менее гибкий.
  • Совместимость:

    • SERIAL проще использовать для быстрого создания автоинкрементных колонок.
    • GENERATED рекомендуется для проектов, где важна поддержка современных стандартов SQL.
  • Миграция:

    • При переносе таблиц из MSSQL в PostgreSQL, строки с IDENTITY обычно преобразуются в SERIAL или GENERATED.

LIMIT и OFFSET#

В MSSQL для этого есть два ключевых подхода:

  1. TOP: Используется для ограничения числа строк.
SELECT TOP 10 * FROM table_name;
  1. OFFSET FETCH: Этот метод позволяет пропустить определенное количество строк и взять фиксированное количество следующих.
SELECT * 
FROM table_name 
ORDER BY column_name 
OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY;

В PostgreSQL для тех же целей используются LIMIT и OFFSET:

  1. LIMIT: Указывает максимальное количество строк, которые нужно вернуть.
SELECT * FROM table_name LIMIT 10;
  1. OFFSET: Указывает, сколько строк нужно пропустить перед возвратом результата.
SELECT * 
FROM table_name 
ORDER BY column_name 
OFFSET 5 LIMIT 10;

Различия:

  • MSSQL требует обязательного использования ORDER BY с OFFSET FETCH, чтобы результат был предсказуемо упорядочен.
  • PostgreSQL не требует ORDER BY для LIMIT и OFFSET, но его использование рекомендуется для получения детерминированного результата.
  • PostgreSQL имеет более простой и интуитивно понятный синтаксис.

Объединение строк#

В MSSQL для объединения строк используется оператор +.

SELECT first_name + ' ' + last_name AS full_name FROM users;
Особенности

Если один из объединяемых элементов равен NULL, весь результат становится NULL. Чтобы избежать этого, можно использовать функцию ISNULL:

SELECT ISNULL(first_name, '') + ' ' + ISNULL(last_name, '') AS full_name FROM users;

В PostgreSQL для объединения строк используется оператор ||.

SELECT first_name || ' ' || last_name AS full_name FROM users;
Особенности

Если один из объединяемых элементов равен NULL, весь результат становится NULL. Чтобы избежать этого, можно использовать функцию COALESCE:

SELECT COALESCE(first_name, '') || ' ' || COALESCE(last_name, '') AS full_name FROM users;
Приведение типов

В PostgreSQL перед объединением может потребоваться явное приведение типов числовых данных к строковым с помощью функции CAST или ::TEXT.

Пустые строки и NULL#

В PostgreSQL пустая строка () и NULL различаются, тогда как в MSSQL они могут трактоваться одинаково в некоторых случаях. Это связано с особенностями настроек совместимости или функций, таких как ISNULL, которые позволяют заменить NULL значением по умолчанию.

В PostgreSQL пустая строка (”) и NULL считаются абсолютно разными понятиями:

  • ” — это строка длиной 0, то есть пустая, но существующая.
  • NULL — это отсутствие значения.

Это различие особенно важно при сравнениях. Например:

SELECT '' IS NULL; -- вернет FALSE
SELECT NULL IS NULL; -- вернет TRUE

Почему это важно при миграции#

Обработка данных:

  • Если в MSSQL колонка содержит пустые строки вместо NULL, то при переносе в PostgreSQL это может вызвать логические ошибки, особенно если приложение ожидает NULL.

Запросы:

  • SQL-запросы, которые используют условия вроде WHERE column IS NULL, могут возвращать разные результаты, если значения из MSSQL были пустыми строками.

Исправление данных:

  • При миграции данных важно привести к единому стандарту: либо заменить пустые строки на NULL, либо наоборот.

Как решить#

  • Перед миграцией данных можно заменить пустые строки на NULL в MSSQL:
UPDATE table_name
SET column_name = NULL
WHERE column_name = '';
  • В PostgreSQL можно использовать функцию COALESCE для замены NULL на пустую строку (или наоборот) при выполнении запросов:
SELECT COALESCE(column_name, '') AS result FROM table_name;

Работа с датами#

В MSSQL для получения текущего времени используется функция GETDATE(). Эта функция возвращает дату и время в формате DATETIME, включающем год, месяц, день, часы, минуты, секунды и доли секунды.

В PostgreSQL используется функция CURRENT_TIMESTAMP. Она возвращает аналогичный результат, включающий дату и время, но в формате TIMESTAMP WITH TIME ZONE (если не указанно иначе).

Различия в форматах и функциях:

  • Тип данных:

    • В MSSQL типы DATETIME и SMALLDATETIME представляют дату и время, но они имеют разную точность и диапазон.
    • В PostgreSQL используется TIMESTAMP (без часового пояса) и TIMESTAMP WITH TIME ZONE, что позволяет явно учитывать часовые пояса.
  • Функции преобразования:

    • В MSSQL для приведения даты к строке используется CONVERT:
    SELECT CONVERT(VARCHAR, GETDATE(), 101); -- MM/DD/YYYY
    
    • В PostgreSQL используется функция TO_CHAR:
    SELECT TO_CHAR(CURRENT_TIMESTAMP, 'MM/DD/YYYY');
    

Сравнение и операции с датами:

  • Добавление или вычитание дат:

    • В MSSQL используется функция DATEADD:
    SELECT DATEADD(DAY, 7, GETDATE()); -- Добавить 7 дней
    
    • В PostgreSQL используется сложение с интервалами:
    SELECT CURRENT_TIMESTAMP + INTERVAL '7 days';
    
  • Разница между датами:

    • В MSSQL используется DATEDIFF:
    SELECT DATEDIFF(DAY, '2023-12-01', '2023-12-31');
    
    • В PostgreSQL это выполняется простым вычитанием:
    SELECT '2023-12-31'::DATE - '2023-12-01'::DATE;
    
Миграция

Если в MSSQL используется DATETIME, его нужно преобразовать в TIMESTAMP PostgreSQL.

Нужно учитывать, поддерживает ли приложение работу с часовыми поясами, так как PostgreSQL различает TIMESTAMP и TIMESTAMP WITH TIME ZONE.

Хранимые процедуры и функции#

В MSSQL хранимые процедуры (stored procedures) создаются с помощью ключевого слова CREATE PROCEDURE. Они используются для выполнения набора SQL-запросов с возможностью передачи параметров. Результаты можно возвращать через параметры или использовать в SQL-запросах.

CREATE PROCEDURE example_procedure
    @param1 INT,
    @param2 NVARCHAR(50)
AS
BEGIN
    INSERT INTO table_name (column1, column2)
    VALUES (@param1, @param2);
END;

Вызов:

EXEC example_procedure 10, 'Sample Text';

В PostgreSQL вместо хранимых процедур чаще используются функции. Они создаются с помощью ключевого слова CREATE FUNCTION. Функции могут возвращать значения (таблицы, отдельные строки, примитивные типы данных и т.д.).

CREATE OR REPLACE FUNCTION example_function(param1 INT, param2 TEXT) RETURNS VOID AS $$
BEGIN
    INSERT INTO table_name (column1, column2)
    VALUES (param1, param2);
END;
$$ LANGUAGE plpgsql;

Вызов:

SELECT example_function(10, 'Sample Text');
Миграция

При переносе хранимых процедур из MSSQL в PostgreSQL их нужно преобразовывать в функции, переписывая синтаксис и учитывая использование PL/pgSQL.

Управление транзакциями#

Что такое транзакция?

Транзакция — это единица работы с базой данных, которая выполняется либо полностью (коммит), либо откатывается в исходное состояние (роллбэк), если происходит ошибка. Это обеспечивает целостность данных.

В MSSQL транзакции начинаются с BEGIN TRANSACTION. Далее выполняются SQL-операции, после которых:

  • COMMIT TRANSACTION фиксирует изменения.
  • ROLLBACK TRANSACTION отменяет все изменения, выполненные с момента начала транзакции.
BEGIN TRANSACTION;
INSERT INTO orders (order_id, customer_id) VALUES (1, 100);
UPDATE customers SET balance = balance - 50 WHERE customer_id = 100;

IF @@ERROR <> 0
BEGIN
    ROLLBACK TRANSACTION;
END
ELSE
BEGIN
    COMMIT TRANSACTION;
END;

Здесь транзакция проверяет наличие ошибок (@@ERROR) перед фиксацией изменений.

PostgreSQL использует схожий подход, но синтаксис проще:

  • BEGIN начинает транзакцию.
  • COMMIT фиксирует изменения.
  • ROLLBACK отменяет изменения.
BEGIN;
INSERT INTO orders (order_id, customer_id) VALUES (1, 100);
UPDATE customers SET balance = balance - 50 WHERE customer_id = 100;

IF (SELECT COUNT(*) FROM customers WHERE balance < 0) > 0 THEN
    ROLLBACK;
ELSE
    COMMIT;
END IF;

Логика проверки ошибок реализуется через SQL или PL/pgSQL конструкции.

Ключевые различия:

  • Синтаксис:
    • MSSQL требует явного указания TRANSACTION, а PostgreSQL использует более короткие команды.
  • Обработка ошибок:
    • MSSQL предоставляет встроенную переменную @@ERROR для проверки ошибок.
    • PostgreSQL предлагает более гибкий механизм управления через блоки EXCEPTION в PL/pgSQL.
  • Автоматическое управление:
    • В PostgreSQL транзакции автоматически обрабатываются в блоках DO или FUNCTION, что делает код более компактным.
Миграция

При переносе логики транзакций с MSSQL на PostgreSQL нужно:

  1. Переписать проверки ошибок в соответствии с механизмами PostgreSQL.
  2. Убедиться, что все зависимости между операциями внутри транзакции правильно адаптированы.
  3. Учесть особенности PostgreSQL, такие как автоматическая фиксация изменений вне явных транзакций.

Заключение#

Миграция с MSSQL на PostgreSQL — это не просто перенос данных, а полноценная адаптация системы к новой платформе. Успешная миграция требует учета множества факторов: от различий в типах данных и синтаксисе SQL до особенностей бизнес-логики и работы с транзакциями.

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

Тщательная подготовка, использование подходящих инструментов и детальное тестирование — ключевые шаги к успешной миграции, которая позволит вам использовать все преимущества PostgreSQL в полном объеме.

В этой статье я постарался описать ключевые различия между системами управления базами данных MSSQL и PostgreSQL, которые следует учитывать при миграции. В следующей части мы перейдём к практическим аспектам и обсудим, как можно осуществить миграцию с использованием готовых решений.

Полезные ссылки по теме#

Миграция с MSSQL на PostgreSQL: Часть 1. Теория
https://devmemo.ru/posts/migraciya-s-mssql-na-postgresql-chast-1-teoriya/
Автор
Артём Плеханов
Опубликовано
2025-01-03
Лицензия
CC BY-NC-SA 4.0