Версия для печати


MS SQL Server + FireBird = Дружба
http://www.delphikingdom.com/asp/viewitem.asp?catalogID=1412

Александр Чмиль
дата публикации 15-09-2009 13:15

MS SQL Server + FireBird = Дружба

Недавно столкнулся с проблемой, когда данные из базы данных FireBird (F) нужно было импортировать в базу данных MS SQL Server 2000 (S). Первой мыслью было написать клиента, который бы подключался одновременно к двум БД и в цикле переносил данные из всех таблиц одной базы в другую. Данный подход мне сразу не понравился тем, что пришлось бы писать на Delphi отдельную процедуру импорта данных под каждую таблицу БД — структура и типы данных у них ведь разные. Кроме того, типы данных у этих БД несколько "не стыкуются", так что пришлось бы учитывать и эту особенность при переносе данных.

За советом я обратился к специалистам на Круглом столе Королевства Delphi — вопрос № 71949. Достопочтенный Green в первом же ответе направил меня совершенно в другом направлении: не клиента писать, а подружить эти две СУБД. Полученный результат настолько меня впечатлил, что я решил написать данную статью.

Итак, для того, чтобы иметь возможность делать запросы из (S) к (F), в первую очередь необходимо установить OLE-провайдер, обеспечивающего связь с БД (F). Поиск в Интернете привел на сайт http://www.ibprovider.com, с которого можно скачать бесплатную версию IBProvider. Функционала данной версии вполне достаточно для выполнения задуманного, хотя никто не мешает приобрести платную версию, но нужно ли это в рамках данной задачи?

Следующим шагом будет регистрация IBProvider в системе. Копируем файлы _IBProvider_v3_free_i.dll и cc3250mt.dll в системную папку Windows (например, в c:\windows\system32) и в командной строке выполняем команду:

regsvr32 _IBProvider_v3_free_i.dll

В результате в списке провайдеров появится LCPI.IBProvider.3.Free.

Хочу заметить, что для "дружбы" (S) с (F) в системе должна быть запущена служба MSDTC (Distributed Transaction Coordinator или Координатор Распределенных Транзакций). В моем случае данная служба была отключена и в упорно не желала запускаться (MS Windows 2000). Поиск в Интернете привел к следующему трюку в командной строке:

net stop msdtc (останавливаем службу msdtc)
net start msdtc (запускаем службу)

При наличии прав администратора в списке служб находим "Координатор распределенных транзакций" и устанавливаем тип запуска "авто".

Все. Теперь мы можем работать с БД (F) как из скрипта MS SQL Server, так и через TADOConnection в Delphi, указав в качестве провайдера LCPI.IBProvider.3.Free. Вот пример содержимого строки ADOConnection.ConnectionString:

Provider=LCPI.IBProvider.3.Free;Password=пароль_к_БД;Persist Security Info=True;User ID=логин_к_БД;
Location=D:\Projects\db\DB.FDB;ctype=ASCII;dialect=3;auto_commit=False;
support_odbc_query=False;unicode_mode=False;unicode_stmt=False;dbclient_library=gds32.dll;
dbclient_type=fb

Вернусь к своей задаче — импорт данных в БД MS SQL Server из БД FireBird. Создаем БД (S) по структуре идентичную БД (F). Из-за различий в типах данных этих двух СУБД, в БД (S) создаем столбцы в соответствии с таблицей приведения типов:

Типы данных (F)Типы данных (S)
timestampdatetime
varcharnvarchar
floatfloat или money
smallint (в роли boolean)bit
blobimage

Не буду расписывать все соответствия. Их Вы можете определить экспериментально или найти информацию в Интернете.

Перейдем к самому интересному — к "дружбе народов" :

Для получения набора данных из внешней БД, в СУБД (S) имеется функция OpenRowSet, синтаксис которой выглядит так:

OpenRowSet ( 'provider_name' 
    , { 'datasource' ; 'user_id' ; 'password' 
        | 'provider_string' } 
    , { [ catalog. ] [ schema. ] object 
        | 'query' } 
    ) 

Описание функции Вы можете найти в Transact-SQL Reference. В нашем случае эта функция используется с таким набором параметров:

OpenRowSet ('provider_name', 'provider_string', 'query')

В качестве первого параметра указывается имя провайдера (LCPI.IBProvider.3.Free), в качестве второго — строка соединения с БД FireBird, третьего — запрос на выборку данных (select * from TableName).

Для импортирования данных на (S) создаем две ХП:

CREATE PROCEDURE ImportFBTable
 @Table nvarchar(30)
AS
BEGIN
 exec('delete from '+@Table)
 exec('insert into '+@Table+' select * from OpenRowSet(''LCPI.IBProvider.3.Free'','''+
  'Password=пароль_FB;Persist Security Info=True;User ID=логин_FB;'+
  'Location=_путь_к_файлу_БД_FB;ctype=ASCII;dialect=3;'+
  'auto_commit=True;support_odbc_query=False;unicode_mode=False;'+
  'unicode_stmt=False;dbclient_library=gds32.dll;dbclient_type=fb'+
  ''',''select * from '+@Table+''')')
END

CREATE PROCEDURE ImportFBData
AS
BEGIN
 exec ImportFBTable 'Таблица_1'
 exec ImportFBTable 'Таблица_2'
 exec ImportFBTable 'Таблица_3'
 ...
 exec ImportFBTable 'Таблица_N'
END

Выполняем процедуру ImportFBData, которая поочередно вызывает процедуру ImportFBTable, передавая ей в качестве параметра имя импортируемой таблицы (естественно, имена таблиц в (F) и (S) должны быть одинаковыми). Процедура ImportFBTable очищает данные в таблице на (S), получает набор данных из таблицы на (F) и вносит их в таблицу на (S). Конечно, при желании можно "заставить" процедуру не удалять данные, а обновлять, но предоставляю Вам самим создать сие действо — в рамках моей задачи это было не обязательно, так как я не использовал в таблицах на (S) внешние индексы.

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

В заключении хотел сказать, что таким образом можно "подружить" MS SQL Server и с другими БД при наличии соответствующего OLE-провайдера. Например, вот тут вопрос № 71737 рассказывается, как можно получить данные с листа MS Excel.

Также хочу выразить огромную благодарность г-ну Александру Зеленову (aka Green), вовремя направившего меня "на путь истинный" и вдохновившего к написанию данной статьи.