Опять об импорте данных

Запросы, планы, оптимизация запросов, ...

Модераторы: kdv, CyberMax

Ответить
Aleksandr.
Сообщения: 63
Зарегистрирован: 18 май 2005, 19:13

Опять об импорте данных

Сообщение Aleksandr. » 16 июн 2005, 13:56

Только не бейте меня, ладно? Я статьи и ссылки все просмотрел, но в итоге только каша в голове образовалась, так ничего для себя и не вывел.

Задача, может быть, и тривиальная: существует система из приложения-сервера данных на MS SQL, к которому подключаются клиентские приложения для обновления информации, все остальное время работающие в оффлайне. Фактически, схема МИДАСа, только своего. Ранее система работала так: клиентские программы используют таблицы Парадокс, при подключении к серверу данных он, при необходимости отдать всю таблицу, а не пакет обновлений, соответственно, создавал для клиента целиковую таблицу Парадокс, экспортируя туда записи из MS SQL обычными insert.

Теперь была поставлена задача от Парадокса избавиться и на клиентах и на сервере. В качестве оффлайновой базы клиента остановились на FireBird 1.5. Встал, соответственно, вопрос, в какой формат сервер данных должен экспортировать таблицы, чтобы они могли быстрее всего создаваться на сервере и быстрее всего перебрасываться клиентом в базу FB. По тестам (из "готовенького") на сервере быстрее всего создаются XML при помощи TClientDataSet (через LocalConnection открывается источник MS SQL и вызывается родной метод SaveToFile). На клиенте, я, не долго думая, реализовал такой код:

Код: Выделить всё

function TATIDB.DoCreateTable(Q: TClientDataSet; aTblName: shortstring; ATime: Integer): boolean;
var
  i,j       : integer;
  s         : string;
  x         : byte;
  t         : string;
  Ot, Os    : string;
  IDT       : smallint;
  sShowName : shortstring;
  function ReseparateField(ss : string) : string;
  var
    st,sv : string;
  begin
    Result:='';
    st:=ss;
    while st<>'' do begin
      if Pos(';',st)<>0 then begin
        sv:=Copy(st,1,Pos(';',st)-1);
        System.Delete(st,1,Pos(';',st))
      end
      else begin
        sv:=St;
        st:=''
      end;
      if Result<>'' then
        Result:=Result+', ';
      Result:=Result+ANSILowerCase(QuotedStr(sv))  
    end
  end;
begin
  Result:=true;
  try
    Q.First;
    s:='';
  //... инициализируются внутренние переменные, места много занимает // и вопроса не касается, поэтому опустим
    {$Region 'creating table'}
      {$Region 'Fill fields'}
    for i:=0 to Q.FieldCount-1 do begin
      if s<>'' then
        s:=s+', ';
      s:=s+'"'+ANSILowerCase(Q.Fields[i].FieldName)+'"';
      case Q.Fields[i].DataType of
        ftFixedChar,
        ftWideString,
        ftMemo,
        ftFmtMemo,
        ftString    : s:=s+' VARCHAR('+IntToStr(Q.Fields[i].Size)+') CHARACTER SET WIN1251';
        ftBlob,
        ftGraphic   : s:=s+' BLOB';
        ftSmallint,
        ftLargeint,
        ftAutoInc,
        ftInteger,
        ftWord      : s:=s+' INTEGER';
        ftBoolean   : s:=s+' SMALLINT';
        ftCurrency,
        ftBCD,
        ftFloat     : s:=s+' FLOAT';
        ftDate,
        ftTime,
        ftDateTime  : s:=s+' TIMESTAMP';
        else
          Continue;
      end;
      if Q.Fields[i].Required then
        s:=s+' NOT NULL';
      if i=0 then begin
        if NOT Q.Fields[i].Required then
          s:=s+' NOT NULL'
      end
      else if i<X then begin
        if NOT Q.Fields[i].Required then
          s:=s+' NOT NULL';
      end;
      case Q.Fields[i].DataType of
        ftFixedChar,
        ftWideString,
        ftMemo,
        ftFmtMemo,
        ftString    : s:=s+' COLLATE PXW_CYRL'
      end;
    end;
    {$EndRegion}
    s:='CREATE TABLE '+ANSILowerCase(aTblName)+' ('+s+')';
    with TIbQuery.Create(nil) do try
      DataBase:=FDB;
      SQL.Clear;
      SQL.Add(s);
      ExecSQL;
      FIT.CommitRetaining;
      {$Region 'Indexing'}
      SQL.Clear;
      s:='';
      for i:=0 to x-1 do begin
        if s<>'' then
          s:=s+', ';
        s:=s+'"'+ANSILowerCase(Q.Fields[i].FieldName)+'"'
      end;
      s:='ALTER TABLE '+ANSILowerCase(aTblName)+' ADD CONSTRAINT PK_'+ANSILowerCase(aTblName)
         +' PRIMARY KEY ('+s+')';
      SQL.Add(s);
      ExecSQL;
      FIT.CommitRetaining;
      s:='';
      if Q.IndexFieldNames<>'' then
        s:='CREATE INDEX '+aTblName+'_IDX1 ON '+aTblName+' ('+ReSeparateField(Q.IndexFieldNames)+')'
      else for i:=0 to Q.IndexDefs.Count-1 do begin
        if Q.IndexDefs[i].DescFields<>'' then
          s:='CREATE DESC INDEX '+aTblName+'_IDX1 ON '+aTblName+' ('+ReSeparateField(Q.IndexDefs[i].DescFields)+')'
        else
          s:='CREATE INDEX '+aTblName+'_IDX1 ON '+aTblName+' ('+ReSeparateField(Q.IndexDefs[i].Fields)+')'
      end;
      if s='' then begin
        for i:=x-1 downto 0 do begin
          if s<>'' then
            s:=s+', ';
          s:=s+'"'+ANSILowerCase(Q.Fields[i].FieldName)+'"'
        end;
        s:='CREATE INDEX '+aTblName+'_IDX1 ON '+aTblName+' ('+s+')'
      end;
      if s<>'' then begin
        SQL.Clear;
        SQL.Add(s);
        ExecSQL;
        FIT.CommitRetaining;
        SQL.Clear
      end;
      {$EndRegion}
      {$EndRegion}
      {$Region 'writing data'}
      SQL.Clear;
      t:='';
      j:=0;
      Ot:='';
      Os:='';



//вот, собственно, начинается главный код импорта:

      while NOT Q.Eof do begin
        s:='';
        t:='';
        inc(j);
        if ot='' then begin
          for i:=0 to Q.FieldCount-1 do begin
            if t<>'' then
              t:=t+', ';
            t:=t+'"'+ANSILowerCase(Q.Fields[i].FieldName)+'"'
          end;
          Ot:=t
        end
        else
          t:=Ot;
        t:='INSERT INTO '+ANSILowerCase(aTblName)+' ('+t+')';
        s:='';
        if Os='' then begin
          for i:=0 to Q.FieldCount-1 do begin
              if s<>'' then
                s:=s+', ';
            s:=s+':'+Q.Fields[i].FieldName
          end;
          Os:=s
        end
        else
          s:=Os;
        t:=t+' VALUES ('+s+')';
        SQL.Clear;
        SQL.Add(t);
        for i:=0 to Q.FieldCount-1 do begin
          if ParamByName(Q.Fields[i].FieldName)<>nil then begin
            if NOT Q.Fields[i].IsNull then begin
              if Q.Fields[i].DataType=ftBoolean then begin
                if Q.Fields[i].AsBoolean then
                  ParamByName(Q.Fields[i].FieldName).AsString:='1'
                else
                  ParamByName(Q.Fields[i].FieldName).AsString:='0'
              end
              else
                ParamByName(Q.Fields[i].FieldName).AsString:=Q.Fields[i].AsString
            end
            else
              ParamByName(Q.Fields[i].FieldName).Value:=Null
          end
        end;
        ExecSQL;
        Q.Next
      end;
      SQL.Clear;
      FIT.CommitRetaining
    finally
      Free
    end
  {$EndRegion}
  except
    on E:Exception do begin
      raise Exception.Create(E.Message)
    end
  end
end;
Работа этого кода продемонстрировала, что все это жутко тормозит, таблицы с 7000-8000 записей перебрасываются до пяти минут (хотя аналогичный код с перебросом XML-Paradox занимает полминуты). И по всем найденным материалам на сайте я так и не смог для себя уяснить, есть ли что-то готовое уже для того, чтобы быстро перебрасывать таблицы между базами клиентов и сервера во взаимопонимаемом формате. Для XML по FB я нашел объект вообще только для Output, есть Input для RowData, но, соответственно, нет соответствующего экспортера у компонент ADODB... Я знаком с FB всего-то пару месяцев, ничего не понимаю в том, что другим при написании статей, видимо, кажется, очевидным...

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 16 июн 2005, 16:49

www.ibase.ru/devinfo/ibx.htm читал? Похоже нет.

несколько замечаний:

1. float надо использовать осторожно. это четырехбайтовое. Если в ms sql другое, то тогда используй double precision

2. FIT.CommitRetaining - зачем? при инсерте надо или делать commit, или не делать. Для DDL вообще commitretaining вызывать нельзя. Только commit.

3. индексы обычно создают ПОСЛЕ импорта, а не до. Это справедливо для любого сервера.

4. Q - ClientDataSet, и через него ты вставку делаешь? Руки поотрубать. Читай статью в самом начале.

Все изложенное в общем справедливо для всех серверов. То есть, если хочется загнуть импорт - сделай так, как ты уже сделал :)

Aleksandr.
Сообщения: 63
Зарегистрирован: 18 май 2005, 19:13

Сообщение Aleksandr. » 16 июн 2005, 18:08

www.ibase.ru/devinfo/ibx.htm читал? Похоже нет.
Читал. Про импорт данных из XML все равно ничего не понятно. То ли искать способ заставить сервер данных быстро сбрасывать таблицы в файл RowData, то ли искать что-то, чем в ран-тайме можно быстро импортировать xml в FB.
несколько замечаний:

1. float надо использовать осторожно. это четырехбайтовое. Если в ms sql другое, то тогда используй double precision

2. FIT.CommitRetaining - зачем? при инсерте надо или делать commit, или не делать. Для DDL вообще commitretaining вызывать нельзя. Только commit.

3. индексы обычно создают ПОСЛЕ импорта, а не до. Это справедливо для любого сервера.
Спасибо, это я учту.
4. Q - ClientDataSet, и через него ты вставку делаешь? Руки поотрубать. Читай статью в самом начале.
Ну что там прочитать? За что руки отрубать? Ну объясните мне, пожалуйста! :(

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 16 июн 2005, 20:37

я там писал про импорт xml? ТЫ БРЕДИШЬ. Я писал про импорт-экспорт через ibbatch. и упоминал, какие компоненты надо использовать для импорта - например IBQuery с insert, а не clientDataSet (именно за это надо по рукам).

IBQuery.SQL.Add('INSERT INTO ....
IBQuery.ExecSQL (или ExecQuery, не помню что там.

Commit делать через n сотен-тысяч инсертов.

p.s. ClientDataSet со своим хранением данных в формате MyBase (и XML) - это вообще ОТДЕЛЬНАЯ песня, к импорту никакого отношения практически не имеющая.

Aleksandr.
Сообщения: 63
Зарегистрирован: 18 май 2005, 19:13

Сообщение Aleksandr. » 16 июн 2005, 21:05

я там писал про импорт xml? ТЫ БРЕДИШЬ. Я писал про импорт-экспорт через ibbatch. и упоминал, какие компоненты надо использовать для импорта - например IBQuery с insert, а не clientDataSet (именно за это надо по рукам).
Понятно. Мы друг друга не поняли. ClientDataSet является источником данных, а не через него они прокачиваются. Процесс начинается с того, что полученный файл xml ClientDataSet делает LoadFromFile. После чего по нему идет перебор записей, для каждой из которых в IbQuery создается строка запроса. Как-то я не подумал, что не все читают паскалевский код.

А насчет бреда, будьте вежливее. Вы написали:
TIBBatch и наследники от этого класса, IBSQL.PAS из IBX
а TIBOutputXML, любым боком относящийся к теме, тоже находится в IBSQL.PAS; ничего про то, что "Вы писали про импорт XML", я не упоминал. Фраза была:
Про импорт данных из XML все равно ничего не понятно.
что и побудило меня спрашивать на форуме, как такой импорт можно осуществить.

Merlin
Динозавр IB/FB
Сообщения: 1502
Зарегистрирован: 27 окт 2004, 11:44

Сообщение Merlin » 16 июн 2005, 21:25

Запутали вы друг друга окончательно и бесповоротно. Вставка у него через IBQuery, а в ClientDataSet он засасывает источник. Рубить руки может и не след, но линейкой по ладошкам - это точно.

1. Чем плох ClientDataSet в качестве источника. А тем, что он кеширует весь набор входных данных. Пока этот набор маленький, в общем-то хрен с ним, но вообще-то это прямой путь отожрать всю память на клиенте прямо на Open и потом долго и нудно жевать сопли со свопом на каждом шаге. Просветлённые в Дао используют в качестве источника либо некеширующий датасет, например, TIBQuery с UniDirectional=True, либо вообще обходятся без датасета, например пользуют TIBSQL. Если источник - не IB/FB, то подбирают соответствующий компонент из нужной области. Или пишут и используют свой метод позаписного чтения без кеширования из источника известного формата.
2. Запрос на Insert упомянутые приблизившиеся к нирване собирают и препарят однократно, вне цикла по данным источника, а не на каждой записи. На каждой записи оне уже только подставляют параметры. Здесь же мы имеем помесь ужа с ежом - на каждой записи сначала заново пересобирается один и тот же текст запроса _с параметрами_, а потом тут же эти параметры подставляются. Придумать такое можно было только под утро бессонной ночи, соболезную, я такого ещё не видел. Любители пересобирать запрос и терять время на препаре на каждом инсёрте обычно всё-таки собирают его Values(...) прямо из значений. А тут задействован считай весь возможный спектр задержек.
3. Стремящиеся к экстремальной быстроходности а) вставку осуществляют через TIBQSL б) к параметрам обращаются по номеру, а не по имени. Но это уже эффекты следующего порядка малости по сравнению с 1 и 2.

Насчёт CommitRetaining и момента создания индексов - согласен с kdv. Особенно за CommitRetainig, пожалуй, линейкой маловато будет.

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 16 июн 2005, 22:15

with TIbQuery.Create(nil) do try
это, конечно сильно. после этой строки еще строк 50 кода. кто так пишет? :)
ClientDataSet является источником данных
так. цитирую себя из упомянутой статьи:

"TIBOutputXML - класс для экспорта в XML."
"эти компоненты не рекомендуется использовать для экспорта. Дело в том, что IBQuery/IBDataSet - кэширующие DataSet-ы...."
согласен, не дописал, но можно было догадаться, что не рекомендуется и ДЛЯ ИМПОРТА. а ClientDataSet - 100% кэширующий компонент.

с ответом N 2 Мерлина согласен на 100%.

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

То есть, специфику IBX и BDE я описал в двух больших статьях. Еще есть общая www.ibase.ru/devinfo/impexp.htm . На мой взгляд можно больше ничего не читать. Или, вряд ли можно найти что либо более специфичное в других местах. Если изложено криво или есть непонятные места - welcome, только в приват, с конкретными претензиями.

Еще в дополнение - в твоем случае, возможно, ClientDataSet в тормозах не виноват. Все зависит от размера импортируемого файла. Проблема скорее во вставке плюс commitRetaining.

Aleksandr.
Сообщения: 63
Зарегистрирован: 18 май 2005, 19:13

Сообщение Aleksandr. » 17 июн 2005, 12:22

kdv:
Цитата:
with TIbQuery.Create(nil) do try
это, конечно сильно. после этой строки еще строк 50 кода. кто так пишет?
А что Вам тут не нравится? Кстати, я, кажется, вник в замечания о кешировании, и сменил это на with TIbSQL.Create.
Вы написали много хороших статей. А нельзя ли в статью об импорте/экспорте (для тех, кто за мной придет :) ) добавить абзац о построчном методе переноса данных? С блок-схемой? Чтобы было просто и наглядно, и люди неопытные сразу срисовывали?

Merlin:
Запрос на Insert упомянутые приблизившиеся к нирване собирают и препарят однократно, вне цикла по данным источника, а не на каждой записи. На каждой записи оне уже только подставляют параметры.
Вот млин. Спасибо Вам! Вы-то меня мордой в самое место и ткнули... Вообще мимо мозга проходило то, что нарисовал... И знал же ведь, да вообще продинамил, дятел (я - дятел)... Щас поправим...

Правда, все-таки в тему с CommitRetaining и Commit я не очень въехал. Разница, как я понимаю, в том, что первая переоткрывается после сброса данных, а вторая закрывается. А если у меня метод1 открывает один набор данных, используя дефолтную транзакцию, а потом внутри себя вызывает метод2 с этим злосчастным переносом таблицы через ту же транзакцию, допустим, в цикле while not eof do importxml, то, чтобы набор данных в методе1 не закрылся и не накрыл женским органом весь смысл while not eof, мне же нужно транзакцию оставить активной? Или для таких случаев надо делать две транзакции - одну на чтение и одну на запись, как упомянуто в статьях? А что вызывать транзакции в том случае, если первый набор данных открывается тоже для редактирования, и в процессе его редактирования редактируется еще какой-нибудь набор данных?


ЗЫ. Уважаемый администратор! А то, что у меня тэги всегда ставятся не по курсору, а в конец текста, а если курсор в конце текста, то за курсором - это у меня Опера так работает с форумом или это так было задумано?

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 17 июн 2005, 14:25

А что Вам тут не нравится?
"нам" тут не нравится то, что любая процедура не должна превышать размер экрана. то есть, чтобы понять, чье свойство SQL, мне надо крутить текст на пару экранов вверх. Собственно, стиль кодирования это личное дело каждого, но есть как бы, общепринятые рекомендации.
А нельзя ли в статью об импорте/экспорте (для тех, кто за мной придет Smile ) добавить абзац о построчном методе переноса данных? С блок-схемой? Чтобы было просто и наглядно, и люди неопытные сразу срисовывали?
не дождетесь. почему если я когда то разобрался в этой элементарщине сам, должен разжевывать это, чтобы девелопер превратится в "девелопера"?
CommitRetaining и Commit я не очень въехал. Разница, как я понимаю, в том, что первая переоткрывается после сброса данных, а вторая закрывается. А если у меня метод1 открывает один набор данных, используя дефолтную транзакцию, а потом внутри себя вызывает метод2 с этим злосчастным переносом таблицы через ту же транзакцию, допустим, в цикле while not eof do importxml, то, чтобы набор данных в методе1 не закрылся и не накрыл женским органом весь смысл while not eof, мне же нужно транзакцию оставить активной?
ClientDataSet-у по барабану транзакция, в которой данные вставляются. я может не понял исходный посыл, но вставлять данные надо в отдельной транзакции. Собственно, я про это писал в ibx.htm - худший случай (пример ibmastapp) - одна единственная IBTransaction, регулярно завершаемая по CommitRetaining. Я тут опять должен разжевать, дать ссылки на статьи, или еще что?
Ну не бывает в этой области знаний, которые можно получать "ПОШТУЧНО". CommitRetaining это плохо потому, что ... транзакции... и т.п.
Или для таких случаев надо делать две транзакции - одну на чтение и одну на запись, как упомянуто в статьях?
блин. не надо бросаться в крайности. В статье (так ты ее читал, или нет) написано, что не надо делать 1 транзакцию на все приложение, и также 2 транзакции на все приложение может оказаться крайностью. Транзакций в приложении должно быть столько, сколько нужно.
Уважаемый администратор! А то, что у меня тэги всегда ставятся не по курсору, а в конец текста, а если курсор в конце текста, то за курсором - это у меня Опера так работает с форумом или это так было задумано?
в нетскейпе и ie все нормально работает. форум стандартный, я его под оперу затачивать не буду.

Merlin
Динозавр IB/FB
Сообщения: 1502
Зарегистрирован: 27 окт 2004, 11:44

Сообщение Merlin » 17 июн 2005, 14:31

Aleksandr. писал(а): А нельзя ли в статью об импорте/экспорте (для тех, кто за мной придет :) ) добавить абзац о построчном методе переноса данных? С блок-схемой? Чтобы было просто и наглядно, и люди неопытные сразу срисовывали?
Вообще-то люди неопытные обычно просто пользуются IBDataPump или ещё какой готовой утилитой. Их писали люди искушённые и накрыть их по быстроходности непросто. Обслуживать желающих срисовать просто скушно. А кому интересно самому научиться и понять, а не срисовывать, следует вникать много во что, а не только в очевиднейший вопрос расхода памяти, глобальных статей для познания Общей Теории Всего за 21 минуту не бывает.
Aleksandr. писал(а):
Правда, все-таки в тему с CommitRetaining и Commit я не очень въехал. Разница, как я понимаю, в том, что первая переоткрывается после сброса данных, а вторая закрывается.
Неправильно понимаешь. Retainig закрывает одну транзакцию и отрывает другую, передавая ей контекст предыдущей. Толку только что другие read_commited транзакции после этого видят изменения, а никакие ресурсы на сервере не отпускаются. Что там получается с DDL операторами, котрые на самом деле выполняются на Commit, мне даже думать не хочется.
Aleksandr. писал(а):
Или для таких случаев надо делать две транзакции - одну на чтение и одну на запись, как упомянуто в статьях?
Ни в коем случае. Статьи пишут либо идиоты-графоманы, либо садисты для того, чтобы читателям навредить. Их надо читать и делать строго наоборот ;) Единственное место для чтения и обновления в одной транзакции - это когда в конкурентной среде требуется обеспечить, обычно при обработке одной записи, исключение по конфликту если с момента нашего чтения до момента нашего обновления запись была модифицирована кем-то другим. При этом применяется уровень изоляции concurrency, Во всех остальных случаях - чтение в одной, обновление в короткой другой.

Aleksandr.
Сообщения: 63
Зарегистрирован: 18 май 2005, 19:13

Сообщение Aleksandr. » 17 июн 2005, 15:38

Млин. Окончательно я запутался с этими транзакциями. :( Пожалуйста, можно мне на конкретных двух тупых примерах, которые я привел:
1. Открывается набор данных для просмотра в цикле while not eof и внутри цикла вызывается метод, в котором происходит вставка записей в другой датасет.
2. Открывается набор данных для изменения, внутри метода его редактирования открывается другой набор данных для изменения.
Сколько в каждом случае должно быть транзакций и когда что применять - Commit или CommitRetaining?

kdv:
"нам" тут не нравится то, что любая процедура не должна превышать размер экрана. то есть, чтобы понять, чье свойство SQL, мне надо крутить текст на пару экранов вверх. Собственно, стиль кодирования это личное дело каждого, но есть как бы, общепринятые рекомендации.
Ну, это Вы уже пургу гоните. Если я в первый раз столкнулся с FB, это не значит, что я в первый раз пишу программу. Со времен Паскаля 5.5 занимаюсь этим, рекомендаций, которые выполняются или херятся, знаю выше крыши, и вдруг узнаю, что нерекомендабельно употреблять with с объектом, на данном участке кода имеющим уникальные свойства и методы...
не дождетесь. почему если я когда то разобрался в этой элементарщине сам, должен разжевывать это, чтобы девелопер превратится в "девелопера"?

Хозяин-барин, конечно. Может, и вправду проще переписываться на форуме по пять раз, а работа подождет...


ЗЫ. Кстати, после исправления ошибки в логике кода, на которую указал Merlin, скорость стала вполне приемлемой. ClientDataSet остался (нету ничего некешируемого, что могло бы его сменить для MyBase xml), TIbQuery сменил на TpFIBQuery, индексы создаются после заполнения таблицы, запрос строится один раз перед началом заполнения, а заполнение идет чисто params.AsString:=Fields.AsString, ExecQuery.

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 17 июн 2005, 17:40

1. Открывается набор данных для просмотра в цикле while not eof и внутри цикла вызывается метод, в котором происходит вставка записей в другой датасет.
это одна транзакция. если источник данных внешний, то транзакции - его собачье дело.
2. Открывается набор данных для изменения, внутри метода его редактирования открывается другой набор данных для изменения.
Сколько в каждом случае должно быть транзакций и когда что применять - Commit или CommitRetaining?
ну е-мое, уже объяснили, что НЕЛЬЗЯ так делать. И в статье об этом написано. Нельзя при массовой вставке "открывать набор для изменения". Здесь надо просто стартовать транзакцию для импорта, читать данные из пункта 1, и заносить их оператором insert в БД. insert-ы оформлять в пакеты по 500-1000 штук, их вызов обрамлять
StartTransaction/Commit.

ты определись с терминологией. То у тебя insert, то теперь оказывается, "набор для изменения".
Ну, это Вы уже пургу гоните.
я с 18-летним опытом программирования пургу гнать не могу по определению :) . Я кода повидал всякого, и статей о стилях кодирования тоже. Увидел нехороший стиль - прокомментировал. И мне Паскалем 5.5 тыкать не надо - я сам на нем писал, и начинал изучать ООП.
И всем известный (?) принцип написания процедурного кода гласит - процедура или функция по количеству строк должна помещаться на экране целиком. По крайней мере, к этому надо стремиться. А уж тем более with, растянутый на 3 страницы - однозначный моветон. Буковок в коде жалко?
скорость стала вполне приемлемой.
ну и чудненько.

Ответить