Форум: "Базы";
Текущий архив: 2007.04.08;
Скачать: [xml.tar.bz2];
ВнизОбмен большими данными между 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;
Скачать: [xml.tar.bz2];
Память: 0.51 MB
Время: 0.056 c