Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2008.01.13;
Скачать: [xml.tar.bz2];

Вниз

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

 
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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.56 MB
Время: 0.008 c
9-1165269799
-=Kirill=-
2006-12-05 01:03
2008.01.13
DirectX


4-1182150503
Dimaxx
2007-06-18 11:08
2008.01.13
Внутренняя конфигурация HDD


15-1197196071
Kostafey
2007-12-09 13:27
2008.01.13
С днем рождения ! 9 декабря


15-1197276822
ArtemESC
2007-12-10 11:53
2008.01.13
А вы ели когда-нибудь насекомых?


2-1197902556
авыф
2007-12-17 17:42
2008.01.13
план выполнения запроса oracle





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский