Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2007.04.08;
Скачать: CL | DM;

Вниз

Обмен большими данными между Oracle и FireBird   Найти похожие ветки 

 
ildarkh   (2007-01-18 15:51) [0]

Существует такая проблема. Есть БД Oracle с таблицей на 500 тыс. записей. Нужно их все перегнать в соответствующую таблицу FireBird. Используется DBExpress.
Я написал с этой целью программу, поначалу крепко зависавшую из-за объема данных(в том числе на сервере с 3ггц Xeon и 4гб памяти). В случае сервера потребление памяти программой выростало до 500 мегабайт. Однако на меньших объемах данных программа работала исправно.
Я изменил код, который отвечает за запрос к Oracle. Но я что-то сделал неправильно. Программа продолжает жрать память и затем зависать. Код ниже, благодарю заранее

procedure TSelectInserts.SelInsExec(selins: IXMLSelectInsertType);
var i: integer;
repwith: string;
errstr: string;
TD: TTransactionDesc;
begin
try
Application.ProcessMessages;
TD.TransactionID := 1;
TD.IsolationLevel := xilREADCOMMITTED;
db2.StartTransaction(TD);
errstr := selins.Select.Query;
self.SelInsWork := 1;

db1.SQLClientDataSet.PacketRecords := 10000;
db1.SQLClientDataSet.FetchOnDemand := false;

db1.SelectQuery(selins.Select.Query, selins.Select.Params);
self.SelInsWork := 2;
i:=0;
while not db1.SQLClientDataSet.Eof do
 begin
 //db1.SQLClientDataSet.First;
 if db1.SQLClientDataSet.RecordCount > 0 then
 //for i:=1 to db1.SQLClientDataSet.RecordCount do
 while not db1.SQLClientDataSet.Eof do
   begin
   inc(i);
   self.InsProgressNum := i;
   errstr := selins.Insert.Query;
   if (i >= 1000 )and (i mod 1000 = 0) then
     begin
     db2.Commit(TD);
     db2.StartTransaction(TD);
     end;
   db2.UpdateQuery(selins.Insert.Query, selins.Fields, db1.DataSource.DataSet.Fields);
   db1.SQLClientDataSet.Next;
   Application.ProcessMessages;
   end;
 if db1.SQLClientDataSet.RecordCount > 0 then
 for i:=0 to selins.Select.Params.Count-1 do
   begin
   repwith := selins.Select.Params.Param[i].Replacewith;
   if(repwith <> "") then
      selins.Select.Params.Param[i].Text := db1.DataSource.DataSet.Fields.FieldByName(repwith).AsString;
 end;
 db1.SQLClientDataSet.GetNextPacket;
 end;
db2.Commit(TD);
xmldoc.SaveToFile(ExtractFilePath(ParamStr(0)) + "XML.xml");
except on e: Exception do
 begin
 db2.Rollback(TD);
 Logger.Write(3, e.Message + ": " + errstr);
 end;
end;
self.SelInsWork := 0;
end;


 
Johnmen   (2007-01-18 16:04) [1]

while not db1.SQLClientDataSet.Eof do
begin

//db1.SQLClientDataSet.First;
if db1.SQLClientDataSet.RecordCount > 0 then
//for i:=1 to db1.SQLClientDataSet.RecordCount do
while not db1.SQLClientDataSet.Eof do
  begin


Это так надо ? :)))


 
Desdechado ©   (2007-01-18 16:14) [2]

Зачем тебе CDS? Он кэширует данные на клиенте.
Тебе же, как я понял, надо построчно читать и тут же записывать.
Вот так и делай. Достаточно SQLDataSet использовать.


 
ildarkh   (2007-01-18 16:19) [3]

Во внешнем цикле проход осуществляется
db1.SQLClientDataSet.GetNextPacket;
Во внутреннем
db1.SQLClientDataSet.Next;

А признак db1.SQLClientDataSet.Eof вроде бы подходит для обоих случаев. Если я не прав, то скажите, в чем?


 
Johnmen ©   (2007-01-18 16:31) [4]

Ты непрв во всём.
Как сказал Desdechado, читаешь записи из входного набора данных, и тут же их пишешь (INSERT INTO). По одной.
Использовать лучше TSQLQuery.


 
ildarkh   (2007-01-18 16:33) [5]

Спасибо, я попробую.


 
ildarkh   (2007-01-18 16:36) [6]

Зачем вообще нужна передача пакетами в TSQLClientDataSet? Я читал, что с помощью них можно поэтапно откачивать данные с сервера БД.


 
Johnmen ©   (2007-01-18 16:42) [7]


> Зачем вообще нужна передача пакетами в TSQLClientDataSet?


Ну, видимо, быстрее пакетами. Хотя, я думаю, это "быстрее" никто не видел :)


 
Desdechado ©   (2007-01-18 17:15) [8]

Про ClientDataSet забудь. Он здесь не нужен в принципе. Достаточно однонаправленного SQLDataSet.

> Зачем вообще нужна передача пакетами в TSQLClientDataSet?
Во-первых, не передача, а прием в CDS.
Во-вторых, это сделано для того, чтобы в случае больших выборок на клиента не замораживать интерфейс вычиткой всей выборки сразу. Может, пользователю будет достаточно поглядеть на первых несколько пакетов, а еще 200 тыс записей ему и не нужны будут. А без пакетирования CDS их сначала все вытянет на клиента (если памяти хватит), а потом разморозит интерфейс.

> Я читал, что с помощью них можно поэтапно откачивать данные с сервера БД.
Правильно. Но только не длятвоего случая. Поскольку CDS запоминает (кэширует) данные на клиенте, а тебе нужно не это.


 
ildarkh   (2007-01-18 19:35) [9]

я изменил исходник по вашим советам, теперь процедура выглядит так
procedure TSelectInserts.SelInsExec(selins: IXMLSelectInsertType);
var i: integer;
repwith: string;
errstr: string;
TD: TTransactionDesc;
begin
try
Application.ProcessMessages;
TD.TransactionID := 1;
TD.IsolationLevel := xilREADCOMMITTED;
db2.StartTransaction(TD);
errstr := selins.Select.Query;
self.SelInsWork := 1;

//db1.SQLClientDataSet.PacketRecords := 10000;
//db1.SQLClientDataSet.FetchOnDemand := false;

db1.SelectQuery(selins.Select.Query, selins.Select.Params);
self.SelInsWork := 2;
i:=0;
//while not db1.SQLDataSet.Eof do
 begin
 db1.SQLDataSet.First;
 //if db1.SQLDataSet.RecordCount > 0 then
 //for i:=1 to db1.SQLClientDataSet.RecordCount do
 while not db1.SQLDataSet.Eof do
   begin
   inc(i);
   self.InsProgressNum := i;
   errstr := selins.Insert.Query;
   if (i >= 1000 )and (i mod 1000 = 0) then
     begin
     db2.Commit(TD);
     db2.StartTransaction(TD);
     end;
   db2.UpdateQuery(selins.Insert.Query, selins.Fields, db1.DataSource.DataSet.Fields);
   db1.SQLDataSet.Next;
   Application.ProcessMessages;
   end;
 if db1.SQLDataSet.RecordCount > 0 then
 for i:=0 to selins.Select.Params.Count-1 do
   begin
   repwith := selins.Select.Params.Param[i].Replacewith;
   if(repwith <> "") then
      selins.Select.Params.Param[i].Text := db1.DataSource.DataSet.Fields.FieldByName(repwith).AsString;
 end;
 //db1.SQLClientDataSet.GetNextPacket;
end;
db2.Commit(TD);
xmldoc.SaveToFile(ExtractFilePath(ParamStr(0)) + "XML.xml");
except on e: Exception do
 begin
 db2.Rollback(TD);
 Logger.Write(3, e.Message + ": " + errstr);
 end;
end;
self.SelInsWork := 0;
end;

Исключения, как видите перехватываются и журналируются. В этой процедуре есть вызов другой процедуры, UpdateQuery, которая записывает данные в БД-получатель
procedure TMSQLConnection.UpdateQuery(query: string; XMLfields: IXMLFieldsType; SQLfields: TFields);
var i: integer;
values: string;
begin
{self.SQLClientDataSet.Close;
self.SQLClientDataSet.CommandType := ctQuery;
self.SQLClientDataSet.CommandText := query;
self.SQLClientDataSet.Execute;}
try
 query := query + "(";
 values := " values (";
 for i:=0 to XMLFields.Count-1 do
   begin
   query := query + XMLFields.Field[i].Db2;
   if(XMLFields.Field[i].Db1 <> "") then
       values := values + #39 + SQLfields.FieldByName(XMLFields.Field[i].Db1).AsString + #39
   else if(XMLFields.Field[i].Value <> "") then
     values := values + #39 + XMLFields.Field[i].Value + #39
   else values := values + XMLFields.Field[i].Func;
   if(i < XMLFields.Count-1) then
     begin
     query := query + " , "; values := values + " , ";
     end;
   end;
 query := query + ")";
 values := values + ")";

 self.SQLDataSet.Close;
 self.SQLDataSet.CommandText := query + values;
 Logger.Write(2, query + values);
 self.SQLDataSet.ExecSQL(false);

except on e: Exception do
 begin
 Logger.Write(3, e.Message + ": " + query + values);
 end;
end;
end;

Эта вызываемая процедура нормально срабатывает в случае небольшой выборки(или возможно по другой причине). Однако в случае большой выборки, которую я упоминал вначале в первой процедуре вываливаются циклически окошки с ошибкой "DBX Error: Operation Not Supported.", не смотря на try...exept. Данные в БД не заносятся, хотя во второй процедуре исключения нет.


 
Desdechado ©   (2007-01-18 21:03) [10]

Такое засорение кода - ужас!
Отладчиком пользоваться умеешь?

ЗЫ зачем такое количество выворотов? там все просто, как рельсы, должно быть


 
ildarkh   (2007-01-19 09:27) [11]

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


 
ANB ©   (2007-01-19 10:41) [12]


> Может есть какие-то более конкретные рекомендации?

Есть. Выкинуть все нафиг и написать заново. Код перекачки из БД в БД - максимум 10 строк (если таблицы одинаковой структуры и данные переливаются один в один, при этом список полей заранее известен, т.е. запросы на чтение и вставку можно подготовить заранее).


 
Desdechado ©   (2007-01-19 10:49) [13]

> Отладчиком пользоваться умею, иначе как бы я написал предыдущий пост.
Не видно. Место ошибки не указал, условия ее возникновения слишком расплывчаты.


 
Sergey13 ©   (2007-01-19 11:01) [14]

> [11] ildarkh   (19.01.07 09:27)

А DataPump не пробовал?


 
ildarkh   (2007-01-19 12:15) [15]


> > Отладчиком пользоваться умею, иначе как бы я написал предыдущий
> пост.
> Не видно. Место ошибки не указал, условия ее возникновения
> слишком расплывчаты.


db1.SQLDataSet.Next;
Application.ProcessMessages;

В этом фрагменте происходит вылет, о котором я выше говорил


> Есть. Выкинуть все нафиг и написать заново.
>

Надо исправить то, что есть, извини



Страницы: 1 вся ветка

Текущий архив: 2007.04.08;
Скачать: CL | DM;

Наверх




Память: 0.51 MB
Время: 0.032 c
15-1173855658
nasty196
2007-03-14 10:00
2007.04.08
panel zada4


15-1174021713
Tirael
2007-03-16 08:08
2007.04.08
Raid 0


2-1174045916
Kolan
2007-03-16 14:51
2007.04.08
А AV в модуле rtl100.bpl что значит ? Дубль два.


2-1174020682
D@Nger
2007-03-16 07:51
2007.04.08
типизированный файл и класс


3-1169032798
Rule
2007-01-17 14:19
2007.04.08
Вопрос знатокам MSSQL Server 2005/2000