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

Вниз

Быстрый вывод в текстовый файл?   Найти похожие ветки 

 
Razrab ©   (2007-12-10 11:53) [0]

Добрый день, коллеги!
Подскажите способ быстрого, непострочного вывода информации в текстовый файл, если таковой вообще возможен в Delphi. Столкнулся со следующей проблемой: вывожу при помощи стандартного построчного вывода в цикле через Writeln() данные из Oracle в текстовый файл. И все бы ничего, но в одном текстовом файле количество строк 300 с лишним тысяч. В результате, создание такого файла у пользователя занимает 1 час с копейками, многовато. Каким образом можно было бы создать текстовый файл не построчно, а одним махом туда запихнуть данные из Select-а?
Заранее благодарю за помощь.


 
Сергей М. ©   (2007-12-10 11:58) [1]

А можно полюбопытствовать, зачем клиенту понадобился такой файл ?


 
Razrab ©   (2007-12-10 12:05) [2]

Это sql-файл набора Insert-ov из таблицы - всего более 300.000 Insert-ov. У клиента нет доступа к нашей БД, и он грузит к себе данные просто запуская набор подобных sql-файлов, которые получает по e-mail.


 
Сергей М. ©   (2007-12-10 12:13) [3]

Показывай в коде, как формируешь файл ..


 
clickmaker ©   (2007-12-10 12:30) [4]


> [2] Razrab ©   (10.12.07 12:05)
> Это sql-файл набора Insert-ov из таблицы - всего более 300.000
> Insert-ov

а импорта-экспорта в Оракле нету? Типа bcp в MS SQL


 
DrPass ©   (2007-12-10 12:44) [5]


> Это sql-файл набора Insert-ov из таблицы - всего более 300.
> 000 Insert-ov.

А не слишком ли нездоровый способ? Может, стоит выполнить bulk load? Наверняка ж он и в Оракле есть.
Тем более что 1 час на выгрузку 300000 строк - это скорее в основном не из-за сохранения в файл, а из-за выгрузки данных из БД.


 
DrPass ©   (2007-12-10 12:51) [6]

Кстати, даже проверил - секунд 20, и при этом еще и с ProcessMessages

procedure TForm1.Button1Click(Sender: TObject);
var
 f: Textfile;
 i: integer;
begin
 AssignFile(f, "c:\txt.txt");
 Rewrite(f);
 for i := 1 to 300000 do
 begin
   writeln(f, "Съешь ещё этих мягких французских булочек да выпей чаю!");
   Button1.Caption:= IntToStr(i);
 end;
 CloseFile(f);
end;


 
palva ©   (2007-12-10 12:52) [7]

Если выводить файл блоками, скажем по 10 кб, то это будет существенно быстрее, чем строками по 80 байт. Сделать это несложно, и вам здесь подскажут, как. Советую, однако, сначала выяснить причину тормозов.


 
DiamondShark ©   (2007-12-10 12:57) [8]

Тормоза точно не из-за метода доступа к файлу.
Скорее, из-за метода построения строки для записи в файл.

Но это гадание на кофейной гуще. Показывай код.


 
Razrab ©   (2007-12-10 14:49) [9]

Вот процедура по которой формируется текстовый файл:

 procedure outfiles(var F: TextFile; ADOqr: TADOQuery);
   var i: integer;
 begin
    ADOqr.Active := True;
    ADOqr.First;
    for i := 0 to ADOqr.RecordCount - 1 do
    begin
      WriteLn(F, ADOqr.FieldByName("LINE").AsString);
      ADOqr.Next;
    end;
    ADOqr.Active := False;
 end;

Коннект через ADO. Похоже что тормоза возникают не из-за построчной записи, а из-за построчной проходки запроса. Но как в данном случае еще можно реализовать, ума не приложу?


 
palva ©   (2007-12-10 14:57) [10]

> Но как в данном случае еще можно реализовать, ума не приложу?
В ADO есть метод Save позволяющий сохранить весь рекордсет в виде XML-файла. Не знаю, будет ли это быстрее. Да еще и обработка этого файла понадобится.


 
Razrab ©   (2007-12-10 15:02) [11]

А что если данные из базы сначала в массив загонять, а уже после из него построчно писать в файл? Есть еще варианты?


 
Сергей М. ©   (2007-12-10 15:03) [12]


> Razrab ©   (10.12.07 14:49) [9]


> ADOqr.RecordCount


Здесь засада №1


> FieldByName


Здесь №2


 
Palladin ©   (2007-12-10 15:07) [13]

ADOqr.Active := True;
   ADOqr.First; // на кой? думаешь курсор после открытия на последней стоит?
   for i := 0 to ADOqr.RecordCount - 1 do // зачем? однопроходный ведь
   begin
     WriteLn(F, ADOqr.FieldByName("LINE").AsString); // лучше по индексу
     ADOqr.Next;
   end;
   ADOqr.Active := False;

пробуй это

ADOqr.CursorLocation:=clUseServer;
ADOqr.CursorType:=ctOpenForwardOnly;
ADOqr.LockType:=ltReadOnly;
ADOqr.Open;
While Not ADOqr.Eof Do
 Begin
  WriteLn(f,ADOqr.Fields[индекс].AsString);
  ADOqr.Next;
 End;
ADOqr.Close;


 
Palladin ©   (2007-12-10 15:09) [14]

надеюсь никакой контрол у тебя к нему не привязан :)


 
Razrab ©   (2007-12-10 15:11) [15]

Сергей М.

А что цикл while not ADOQuery.Eof do и обращение к полю по индексу будут быстрее?! %) Или я не так понял Ваш намек?


 
Palladin ©   (2007-12-10 15:13) [16]


> Razrab ©   (10.12.07 15:11) [15]


> обращение к полю по индексу будут быстрее?

ну так надо думать то головой... то ли объекту искать поле по имени в цикле по всем полям сравнением, да еще и с Case-Insentivity, то ли обращатся на прямую к полю в массиве


 
{RASkov} ©   (2007-12-10 15:15) [17]

> [15] Razrab ©   (10.12.07 15:11)

Лучше ответь на [14])
Может у тебя грид скролится во время вывода....


 
Razrab ©   (2007-12-10 15:18) [18]

Palladin


> надеюсь никакой контрол у тебя к нему не привязан :)


Ну как? Сам коннект работает через ADOConnection и ADOQuery, размещенные на форме. Поскольку запросы весьма громоздкие, то в ADOQuery Select-ы занесены в свойство SQL, чтобы кучу всяких кавычек не ставить.


 
Сергей М. ©   (2007-12-10 15:19) [19]


> Razrab ©   (10.12.07 15:11) [15]


По поводу RecordCount - свойство возвращает гарантированно правильный результат только в том случае, если предварительно был выполнен метод Last.

По поводу обращения к объекту-полю по индексу - да, и ощутимо.


 
Palladin ©   (2007-12-10 15:20) [20]


> Razrab ©   (10.12.07 15:18) [18]

имеется ввиду XXXDBGrid семейство


 
Palladin ©   (2007-12-10 15:22) [21]


> Сергей М. ©   (10.12.07 15:19) [19]

отнюдь не всегда... во первых провайдер должен поддерживать его, во вторых зависит от кучи (ну на самом деле не такой уж и большой кучи) настроек соединения и объекта RecordSet, например в указанных в моем примере настройках RecordSet"а RecordCount не вернется... по крайней мере это справедливо для Jet и MSSQL


 
Palladin ©   (2007-12-10 15:24) [22]

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


 
Razrab ©   (2007-12-10 15:25) [23]

Palladin

А почему while то быстрее чем for?


 
DiamondShark ©   (2007-12-10 15:26) [24]


> Razrab ©   (10.12.07 15:25) [23]
> Palladin
>
> А почему while то быстрее чем for?

Он не быстрее, он правильнее.
Как тебе уже сказали, RecordCount после открытия не обязан возвращать точное количество записей.


 
{RASkov} ©   (2007-12-10 15:27) [25]

> [23] Razrab ©   (10.12.07 15:25)

Не быстрнее....
Только если ты не заметил, то разговор не об этом....)


 
Razrab ©   (2007-12-10 15:27) [26]

Palladin

DBGrid-ов у меня нет.


 
sniknik ©   (2007-12-10 15:27) [27]

> надеюсь никакой контрол у тебя к нему не привязан :)
неважно, посмотри реализацию компонент, при явном отключении проверка на одном, верхнем уровне, при отсутствии контрола "на пару этажей ниже".
и раз уж идет борьба за скорость...

в общем советую в любом случае отключать. как и сменить ADOQuery на ADODataSet... советую, советую... а толку нет.


 
{RASkov} ©   (2007-12-10 15:29) [28]

> [26] Razrab ©   (10.12.07 15:27)

Усё равно DisableControls не помешает)


 
Razrab ©   (2007-12-10 15:46) [29]

И еще, не пойму как иногда вообще без RecordCount обходиться?! %) Ведь скажем, при выводе в тот же Excel, у которого ограничение на 65000 строк на лист, куда быстрее после Open узнать кол-во записей сразу по RecordCount, нежели перед одним полным Select-ом еще и Select Count(*) выдавать. Смысл?


 
DrPass ©   (2007-12-10 15:53) [30]


> Сергей М. ©   (10.12.07 15:19) [19]
>
> > Razrab ©   (10.12.07 15:11) [15]
>
>
> По поводу RecordCount - свойство возвращает гарантированно
> правильный результат только в том случае, если предварительно
> был выполнен метод Last.

У ADODataset оно, в отличие от других датасетов, все-таки показывает именно количество записей в наборе, а не количество выфетченных записей. Насколько я знаю, потому что ADODataset в двунаправленном режиме не поддерживает страничный фетч, а сразу тупо фетчит на себя весь датасет.


 
DrPass ©   (2007-12-10 15:55) [31]


> Ведь скажем, при выводе в тот же Excel, у которого ограничение
> на 65000 строк на лист, куда быстрее после Open узнать кол-
> во записей сразу по RecordCount

Не вижу связи первого со вторым


 
Palladin ©   (2007-12-10 15:58) [32]


> Razrab ©   (10.12.07 15:46) [29]

Ты не представляешь на сколько редки подобные ситуции и в связи с их редкостью абсолютно не в падлу сделать count(*)


 
Сергей М. ©   (2007-12-10 16:04) [33]


> DrPass ©   (10.12.07 15:53) [30]


Сегодня ADODataset, завтра еще какой-нить XXXDataset - рано или поздно грабли выстрелят. Именно на этом я и заострил внимание во фразе "засада №1".
Ни более ни менее. Работает оно сейчас у Автора - да ну и слава богу)


 
Palladin ©   (2007-12-10 16:08) [34]


> sniknik ©   (10.12.07 15:27) [27]

когда то замерял

ADOQuery и ADODataSet на MSSQL и Jet, абсолютно равны по производительности, при ltReadOnly,clUseServer и ctOpenForwardOnly

:) более того на Jet если делать просто тупой цикл
While Not q.Eof Do q.Next;
ADOQuery в два раза быстрее :) ну это понятно ерунда хоть и прикольно... :)


 
DrPass ©   (2007-12-10 16:51) [35]


> ADOQuery и ADODataSet на MSSQL и Jet, абсолютно равны по
> производительности

Логично, т.к. первый класс от второго отличается только тем, что CommandText выведен в свойство SQL, в остальном они тождественны


 
Razrab ©   (2007-12-10 16:57) [36]

Спасибо, коллеги, ВСЕМ кто пытался помочь и реально помог, сейчас все заработало практически со скоростью самих запросов! :) А лекарство вот, напоминаю вышеприведеннный уважаемым Palladin-ом код:

ADOqr.CursorLocation:=clUseServer;
ADOqr.CursorType:=ctOpenForwardOnly;
ADOqr.LockType:=ltReadOnly;
ADOqr.Open;
While Not ADOqr.Eof Do
Begin
 WriteLn(f,ADOqr.Fields[индекс].AsString);
 ADOqr.Next;
End;
ADOqr.Close;


Так что, если еще кто на подобные грабли наступит ответ готов и выделен жирным. ;)


 
slavakaram   (2007-12-10 17:13) [37]

Есть бесплатный компонент kbMemTable - Потомок Dataset. Можно воспользоваться им. Единственно, что он возможно выводит все в CSV-формате, потому максимум 65536 строк. Но зато быстро


 
Razrab ©   (2007-12-10 17:17) [38]

Palladin


> Ты не представляешь на сколько редки подобные ситуции и
> в связи с их редкостью абсолютно не в падлу сделать count(*)


У нас как раз бывает частенько. :) Юзер хочет видеть данные из БД в Excel, при это заранее даже не задумываясь об их количестве и естественно ограничении самого Excel. Выполнение трехэтажного Select для вывода таких данных порой может доходить до 30-40 минут. Если еще запустить count(*) на такой же промежуток времени, они очумеют. :) Поэтому предпочитаю RecordCount, благо дело под Oracle он вроде без проблем работает.


 
Palladin ©   (2007-12-10 17:19) [39]


> Razrab ©   (10.12.07 17:17) [38]

так решается просто...
прямо в запросе select top 65000


 
Razrab ©   (2007-12-10 17:28) [40]

В Oracle top нет. Там можно правда через Rownum вложенно выводить от сих до сих. Но я когда увидел, что RecordCount работает, сделал через него. Правда выше уже коллеги напугали, мол до поры до времени. %) Ну на всякий случай буду иметь в виду. :)



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

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

Наверх




Память: 0.57 MB
Время: 0.017 c
2-1197731368
q1485
2007-12-15 18:09
2008.01.13
Полный путь директории


11-1182516150
Nikfel
2007-06-22 16:42
2008.01.13
Алгоритм перебора символов.


2-1197367826
rich_rich
2007-12-11 13:10
2008.01.13
работа с BLOB в MySQL (ZQuery)


15-1197295580
slavakaram
2007-12-10 17:06
2008.01.13
Массовая рассылка почты с помощью idSMTP


2-1197379394
cvg
2007-12-11 16:23
2008.01.13
Как заставить чарт отображать только последний час?