Начало » Использование СУБД » PostgreSQL » Перевод с MS SQL
Перевод с MS SQL [сообщение #137] |
Tue, 05 July 2022 12:02 |
BlackEric
Сообщений: 362 Зарегистрирован: June 2022
|
Senior Member |
|
|
Как бы вы переводили большую систему с MS SQL на Postrgre?
Вручную пересоздали таблицы, затем переписали все запросы, а потом уже перелили скриптами данные?
Кто-то такое делал? Можете поделится опытом?
|
|
|
|
|
|
|
|
Re: Перевод с MS SQL [сообщение #274 является ответом на сообщение #171] |
Sat, 06 August 2022 21:32 |
vdeltsov
Сообщений: 3 Зарегистрирован: August 2022
|
Junior Member |
|
|
Добрый день. Поделюсь некоторыми мыслями.
Занимался переводом базы с SyBase SQL Anywhere на MS SQL Server.
1. Создал все таблички в новом сервере. У меня было всего 200 таблиц, так что я просто создал скрипт таблиц со старого сервера, и с помощью Find & Replace в блокноте переделал несовместимые типы. Это делается недолго.
2. Все индексы на всех таблицах пересматривал заново. От некоторых больших индексов отказался, что-то переделал. Так как знаю, какие обычно используются запросы и какие индексы могут понадобится. Где-то в индексах поставил условие where. Это в случае, если индекс требуется не на всю таблицу, а лишь на относительно небольшую часть строк.
3. Создал все функции. Сложно была в том, что в MS SQL Server в запросах обязательно писать dbo.FuncName, а ранее писалов просто FuncName. Пришлось дописывать dbo.
4. Вторая сложность - в MS SQL Server невозможно к функции добавить необязательный параметр, которых можно не указывать. Почему-то необязательные параметры обязательно надо указывать default при вызове. Это прямо бесит. Хочешь добавить опциональный параметр - приходится делать новую функцию. И постепенно избавляться от старой. Либо в старой делать вызов новой. В общем лишний геморрой. К слову с процедурами это работает - необязательный параметр можно не указывать.
5. Создать одинаковые сервисные функции. Например, получить дату без времени, или время без милисекунд. В оракле дата - это trunc(sysdate), в MS SQL cast(getdate() as date). Делаем функцию dbo.f_GetToday() и на старом на новом сервере. И везде в запросах внешнего приложения меняем вызов.
6. С вьюшками были сложности, что в SyBase можно сделать расчетное поле, и тут же его использовать для расчета соседнего поля, а тут пришлось делать select from (select from)
7. Перепиливаем все процедуры. Заодно удаляем неиспользуемый функционал.
В итоге получаем, что код внешнего приложения работает на обоих серверах.
Однако перевести все разделы сразу сложно и рискованно. Плюс еще были внешние приложения, которые работают с базой, где другие разработчики, поэтому перевести всех в час иск одновременно - это с ума сойти. У одного двоих что-то не заработает, и будет как угорелый бегать, думать что делать.
Поэтому я сделал синхронизацию баз.
В каждой базе на каждой таблице сделал триггер (для создания триггеров написал скрипт), который в отдельную таблицу кладет готовый запрос insert, update, delete, который повторяет все изменения. То есть в таблице получается как бы журнал изменений. Далее раз в 10 секунд (можно и чаще) job со стороны MS SQL копирует туда изменения, созданные здесь. А от туда забирает изменения созданные там.
Внешние системы все перевел (на это ушло не один месяц) на соединение к новому серверу (у меня это MS SQL).
Благо внешние системы использовали только процедуры. Поэтому я просто в начале каждой процедуры проверял, является ли этот сервер ведущим, и если не является, то запускал через Linked Server такую же процедуру на старом сервере.
Внешние системы на тестовой базе тестировали функционал нового сервера, а на боевой базе они изначально выполнялись в конечном счете на старом сервере.
Теперь на счет разделов. В базе пара десятков относительно независимых разделов и перетащить все сразу тоже не рискнул. Поэтому я поделил все таблицы на разделы, и переводил их по одному на новый сервер. Начал со справочников. А в приложении в каждом запросе указывал - что за раздел. И исходя из этого выбирал сервер. Но если Вы готовы рискнуть. То можно сначала всё адаптировать для нового сервера, а потом разом переключить соединение.
На новый сервер изначально данные копировал через Linked server целиком, а далее синхронизировал с отставанием на 10 секунд. Можно время и уменьшить до 1 секунды. На ведомом сервере вполне можно делать отчеты, скачивать данные в хранилище и т.п. А также вполне можно делать в таблицах update и delete. Insert на ведомом сервере запретил в том же триггере, что пишет в журнал событий. Insert я разрешил только на ведущем сервере, так как были автоинкреметные поля ID. но при желании и это можно перепилить на sequence. На одном сервере выдавать только четные, а другом - только нечетные записи.
Когда вся функциональность всех систем допилена под новый сервер, то просто переключаю ведущий сервер. И у внешних систем те же самые процедуры на новом сервере выполняются прямо тут, без трансляции на старый. А мое приложение, просто отправляет запросы на новый сервер. Если вдруг что-то пойдет не так, то просто переключаю ведущий сервер на старый, и все работает на старом сервере, не потеряв ни одной строки данных.
Недавно друг говорил, что делал доклад на конференции от Oracle к PostgreSQL - путь длиною в четыре года.
У нас тоже пробуют переводить ORACLE на PostgreSQL. Некоторые сложности появляются.
1. Например, если у вас обычное клиент-серверном приложение. exe-шник плюс база. То может будет проблема с коннектами. Если просто создать 1800 коннектов к базе, то всё накрывается медным тазом. В обучающем курсе по PostgreSQL тоже говорили, что 1000 коннектов - это уже много. В частности из-за того, что под каждый коннект создается отдельный процесс на операционной системе.
2. Надо делать пулл соединений. Например, 100 коннектов на всех. Это накладывает ограничения. Например, если коннект открыл длинную транзакция, то коннект резервируется и свободных остается 99 штук.
Сложнее с временными таблицами. С ними вообще всё по другому.
Также при пулле коннектов Вы не можете использовать ограничение доступа к таблицам/процедурам. Будет один технологический пользователь. Может это и не страшно, но если в коде используется в запросах "текущий пользователь", то это уже работать не будет.
Отдельная сложность с курсорами. Правда зависит от языка, на котором программируете. в MS SQL можно просто написать процедуру, в которой написано: select * from Clients; select * from City. А на клиенте получить этот recordset (или даже сразу два через) как будто просто написали select, а не вывоз процедуры. С PostgreSQL придется исхитряться либо c refcursor, либо переделывать на функции, к которым обращаться типа так: select * from FuncName(param1, param2).
В общем если использовали select/insert/update/delete прямо на клиенте, или использовался ORM framework (что с точки зрения базы тоже самое), то будет легко перенести, а если процедуры - то на каждой придется возиться.
Собственно у меня большая часть запросов была прямо с клиента, их перепиливать практически не пришлось - только небольшая косметика. А процедуры - каждую пилить вручную.
С триггерами особых проблем нет, но их точно также перепиливать вручную каждый.
Если что - обращатесь.
[Обновления: Wed, 10 August 2022 17:41] Известить модератора
|
|
|
Re: Перевод с MS SQL [сообщение #275 является ответом на сообщение #274] |
Sat, 06 August 2022 21:35 |
vdeltsov
Сообщений: 3 Зарегистрирован: August 2022
|
Junior Member |
|
|
А вот пример триггера, который заполняет таблицу журнала для изменений в другую базу.
Этот механизм собираюсь оставить, чтобы с его помощью делать резервный сервер с отставанием всего 10 секунд,
и не заморачиваться с репликацией, которая у каждого поставщика базы своя, и никто не знает как работает.
При желании в триггер можно добавить генерацию откатывающего запроса. Чтобы можно было получить состояние базы на любой момент времени.
PS: truncate на таблицах с данными не использовался. Только на временных таблицах, которые не требуется реплицировать.
ALTER TRIGGER [dbo].[Branches_SybaseRepl]
ON [dbo].[Branches]
FOR INSERT, UPDATE, DELETE
AS
BEGIN
if dbo.f_IsTableMsSql('Branches') = 0 and dbo.f_GetUserDB() != 'PLREPL' and (select top 1 1 from inserted) is not null and (select top 1 1 from deleted) is null
begin
rollback transaction;
raiserror ('В таблицу Branches нельзя вставлять данные в эту БД.', 16, 1);
return;
end
-- delete or update Primary Key column
if (select top 1 1 from deleted) is not null
insert into dbo.AdmReplToSybase(TableName, sqlText)
select 'Branches', concat(
'delete dbo.Branches where "PrID"=', convert(varchar(max), t."PrID"))
from deleted t
where not exists (select 1 from inserted a where a."PrID" = t."PrID")
-- insert or update Primary Key column
if (select top 1 1 from inserted) is not null
insert into dbo.AdmReplToSybase(TableName, sqlText)
select 'Branches', concat(
'insert dbo.Branches ("PrID"
,"Name")
values (
',isnull(convert(varchar(max), t."PrID"),'null')
,',',isnull(''''+dbo.f_qs(t."Name")+'''', 'null'),')')
from inserted t
where not exists (select 1 from deleted a where a."PrID" = t."PrID")
-- update columns except Primary Key
if (select top 1 1 from deleted) is not null and (select top 1 1 from inserted) is not null
insert into dbo.AdmReplToSybase(TableName, sqlText)
select 'Branches', concat(
'update dbo.Branches set ', substring(sqlText, 2, len(sqlText)-1))
from (select concat(''
,case when update("Name") then ',"Name"='+isnull(''''+dbo.f_qs(t."Name")+'''', 'null') else '' end
, ' where "PrID"=', convert(varchar(max), t."PrID")) as sqlText
from inserted t
where exists (select 1 from deleted a where a."PrID" = t."PrID")
) tt
END
[Обновления: Sat, 06 August 2022 21:50] Известить модератора
|
|
|
|
Переход к форуму:
Текущее время: Sat Nov 23 23:17:12 GMT+3 2024
Общее время, затраченное на создание страницы: 0.00856 секунд
|