Newsgroup: it.comp.lang.delphi
Titolo: AcuCobol,Esportazione e BorlandDB

Ciao a tutti ..
Tempo fa avevo scritto un messaggio chiedendo come si poteva interfacciare
un data base gestito da acucobol in delphi.
Il formato del DB e' proprietario ovvero chi ha scritto il programma si e'
inventato la struttura e, dopo contatto verbale, non ha la minima idea di
rilasciare questa struttura.
Pero' il programma in questione tramite una procedura puo' esportare quasi
tutti i campi del DB in un file di testo ..... cosi' ho fatto.
Il risultato e' stato l'esportazione di circa 600.000 record in un file di
testo dove viene indicato (riga per riga) il codice del prodotto con il nome
del prodotto e il suo prezzo, ogni campo separato cun un ';' (punto e
virgola).
A questo punto per testare il tutto ho esportato 50.000 record (circa 30MB
di file di testo) e ho fatto una procedura per recuperare i dati in oggetto,
ovvero leggo una riga recupero i dati delimitati con il ; tolgo gli spazi
(se ci sono), con una locate verifico se il codice e' o meno presente nel DB
e se non lo e' lo aggiungo.
Per processare 50.000 record impiega circa 20 minuti ..... si puo'
accellerare ????
Grazie
Ciao
Daniele

accellerare ????
E' probabile sia migliorabile ma dimentichi di darci alcune
informazioni fondamentali: che DB usi? come controlli se il record è
già presente? è lenta la routine di inserimento, quella di controllo o
quella di parsing de file di testo?
-- 
"La velocità luce non è sufficiente Comandante Nunziatella, 
dobbiamo passare alla velocità smodata!"

accellerare ????
E' fondamentale vedere il codice che effettua questa operazione.
Facendo i conti della serva: 50000 record in 20 minuti equivalgono a
processare 41 record al secondo, non male direi.... cmq posta il codice che
vediamo se e' ottimizzabile. Descrivi anche la struttura del record che leggi
e fornisci qualche riga campione se puoi. piu' infos ci dai, piu' facile sara'
spremere l'algoritmo fino all'ultimo ciclo di cpu eh eh....
Ciao
-- 
Morde. 
(
mo...@despammed.com
)

 spremere l'algoritmo fino all'ultimo ciclo di cpu eh eh....
Hai ragione ..... ecco quello che serve
1) Stralcio del file di testo dove prendo le informazioni (non so come si
vede nel vostro messaggio .... ma ovviamente i vari ";" sono incolonnati tra
loro !!!
    Qui la formattazione si e' persa per via dei caratteri #32 !!! )
907123739    ;8007659007000;Molla a spirale 10Kg          ;     30,00
907124325    ;8007659007024;Set di chiavi inglesi         ;     15,00
900444288    ;             ;OR 0,8                        ;      0,50
900444201    ;             ;Set guarnizioni per pompa 10MT;    160,00
907123715    ;8007659007017;Spinetta di giunzione pompa 10;      0,15
Dove la prima colonna da il primo codice, la seconda colonna da il codice
alternativo, la terza la descrizione e l'ultima il prezzo.
2) Procedura con cui sistemo il tutto (o quasi ..... va bhe si puo' fare di
meglio !! )
    Stesso problema di prima per la formattazione ....
procedure TGDB.GoBtnClick(Sender: TObject);
Var F        : TextFile;
    S        : String;
    Code1    : String;
    Code2    : String;
    Nome     : String;
    WS       : String;
    LineRead : Integer;
    LineSkip : Integer;
    LineAdd  : Integer;
    E        : Integer;
begin
  GoBtn.Enabled:=False;
  // Assegno il file ......
  AssignFile(F,GF.FileName); // e' il file di testo che leggo
  {$I-}
  Reset(F);
  E:=IoResult;
  {$I+}
  If E<>0 Then Close // Errore ...
  Else
  Begin // Converto ...
    With TDB Do  // Assegno il DB che nel mio caso e' rappresentato da una
tabella
    Begin
      Active:=False;
      DatabaseName:='C:\'+WorkDir;
      TableName:=ArticoliDB;
      TableType:=ttParadox;
      If Not Exists Then
      Begin
        CreaTabella(TDB); // NewCode.Pas .... creo la tabella ... nel caso
il programma per qualche motivo non la trova ..
      End;
      Active:=True;
    End;
    LineRead:=0; // sono tre indicini per effettucci grafici a scopo
statistico
    LineSkip:=0;
    LineAdd:=0;
    ResetTime;
    StopConvertion:=False;
    CancelBtn.Caption:='&Ferma';
    Timer1.Enabled:=True;
    SL.Visible:=True; // e' il tempo che visualizzo per capire quanto tempo
impiega a fare il lavoro e' aggiornata nell'evento ontimer di Timer1
    While Not EOF(F) Do  // ciclo
    Begin
      // Creo l'uscita tramite la pressione di ESC
      ConvertionStarted:=True;
      Application.ProcessMessages; // GRAZIE A TUTTI PER IL SUGGERIMENTO
      If StopConvertion Then
      Begin
        Stop; // e' dove resetto tutto
        SL.Visible:=False;
        Break;
      End;
      Inc(LineRead);
      WS:=IntToStr(LineRead);
      If Length(WS)<6 Then WS:='0'+WS;
      CL.Caption:=WS;
      CL.Repaint;
      ReadLn(F,S);
      Code1:='';
      Code2:='';
      Nome:='';
      // Code1 ovvero recupero il primo codice
      Code1:=Copy(S,1,Pos(';',S));
     // Forse qui mi potete dare una mano per ottimizzare
      If Code1[Length(Code1)]=';' Then Delete(Code1,Length(Code1),1);
      While (Code1<>'') And (Code1[Length(Code1)]=#32) Do
Delete(Code1,Length(Code1),1);
      // Controllo se esiste gia' nel DB controllando il primo codice
      TDB.First; // e' necessario posizionarsi sempre sul primo record ??
      If Not TDB.Locate('Codice',Code1,[loCaseInsensitive]) Then // nel caso
non trovo il record ... allora lo aggiungo processando il resto della riga
      Begin
        Delete(S,1,Pos(';',S)); // tolgo il primo pezzo della riga cosi'
rimane solo il resto cioe' secondo codice,descrizione,prezzo
        // Code2
        Code2:=Copy(S,1,Pos(';',S));
        If Code2[Length(Code2)]=';' Then Delete(Code2,Length(Code2),1);
        While (Code2<>'') And (Code2[Length(Code2)]=#32) Do
               Delete(Code2,Length(Code2),1);
        Delete(S,1,Pos(';',S));
        // Articolo
        Nome:=Copy(S,1,Pos(';',S));
        If Nome[Length(Nome)]=';' Then Delete(Nome,Length(Nome),1);
        While (Nome<>'') And (Nome[Length(Nome)]=#32) Do
Delete(Nome,Length(Nome),1);
      //  Delete(S,1,Pos(';',S));
        Prezzo=Copy(S,1,Pos(';',S));
        If  Prezzo[Length(Prezzo)]=';' Then Delete(Prezzo,Length(Prezzo),1);
        Inc(LineAdd);
        WS:=IntToStr(LineAdd);
        CA.Repaint;
        If Length(WS)<6 Then WS:='0'+WS;
        CA.Caption:=WS;
        TDB.Append;
        TDB.FieldByName('Articolo').AsString:=Nome;
        TDB.FieldByName('Codice1').AsString:=Code1;
        TDB.FieldByName('Codice2').AsString:=Code2;
        TDB.FieldByName('Prezzo').AsString:=Prezzo2;
        TDB.Post;
      End
      Else
      Begin
        Inc(LineSkip);
        WS:=IntToStr(LineSkip);
        If Length(WS)<6 Then WS:='0'+WS;
        CI.Caption:=WS;
        CI.Repaint;
      End;
    End;
    Stop;
    TDB.Close;
  End;
end;
Ecco il tutto ......
Speriamo che una tabella possa gestire circa 600.000 record .... altrimenti
sono al punto di partenza (nel caso quale strumento free ci sarebbe per
poter rimpiazzare gli strumenti del Delphi nel campo DB ??)
Grazie
Ciao
Daniele
Ciao

If Not TDB.Locate('Codice',Code1,[loCaseInsensitive]) Then // nel caso
600.000 locate sono il collo di bottiglia
Ad intuito proverei a prendere in esame una struttura di
acquisizione/inserimento simile:
1- Usa un clientdataset per leggerti i record e trattenerli in memoria
(guadagni in velocita' e flessibilita')
2- non fare la locate ma usa un try..except sulla insert, intercettando
l'errore di chiave duplicata e quindi scartando il record. Cosi' facendo
elimini 600.000 locate, ma le 600.000 insert che fai dovrebbero essere piu'
veloci
3-Ci sono molte altre possibili migliori soluzioni, ad esempio cambiare
database. Oppure lavorare interamente in memoria con i clientdatasets.
Aspetto di vedere se hai altri feedback dai colleghi del ng, intanto prova a
ottimizzare il trattamento dell'acquisizione dei dati dal file di testo.
Mi dispiace vorrei seguirti di piu' ma sono troppo preso dal mio sviluppo, ed
essendo insorti degli imprevisti, mi tocca lavorare anche domani. Seguo
comunque costantemente il ng. 
Buon lavoro,
-- 
Morde. 
(
mo...@despammed.com
)

loro !!!
[SNAPPONE]
Come ti ha giustamente detto Morde, non controllare se il record
esiste ma inseriscilo e trappa l'eventuale chiave duplicata, già
questo ti farà risparmiare parecchio tempo.
Altro consiglio: è deleterio fare 600.000 appplication.processmessages
e altrettanti repaint. Consideranto che in un secondo dovresti
riuscire ad elaborare 50-100 record e visto che usi dei contatori io
farei una cosa del genere:
while
   ......
  if RecRead mod 100 = 0 then begin
    Application.ProcessMessages;
   (reapaint e affini)
  end;
  ........
end;
Avrai un leggero ritardo nell'intercettare l'ES ma in questo modo
dovresti migliorare ulteriormente le performances.
ciao.


 poter rimpiazzare gli strumenti del Delphi nel campo DB ??)
Con Paradox non è solo una questione di numero di record ma anche di
lunghezza degli stessi. Potresti ad esempio usare i componenti ADO ed
Access, con la garanzia che ci perdi in velocità.
Ultima precisazione relative alle tabelle: il numero e la struttura degli
indici influiscono molto sulle performance di scrittura dei singoli record.
Quindi, in relazione alle specifiche esigenze, fai anche attenzione a come è
strutturata la tabella di destinazione.
Avessi modo fare delle prove, potrei essere un pò piu preciso.
Ciao,
Gianluca.

 (
mo...@despammed.com
)

Ciao ...
        sono sul disperato andante !!!!
Ho rimpiazzato la TTable con un componente shareware (TinyTable) che e' vero
che non ha limiti di record (i miei 600.000 record ci stanno tutti) ma in
fase di interrogazione e' un lumaca (la lumaca va piu' forte !!!), infatti
usando l'utility che c'e' a corredo per fare una ricerca sui record ... se
il codice inserito corrisponde all'ultimo .... impiega la bellezza di 15
secondi con la locate, 8 secondi con il goto, e 8 secondi con il findfirst.
Il buon vecchio TTable sui 314.000 che mi permette di inserire impiega, con
una locate, un po piu' di un secondo (comumque sempre lento).
Ecco cosa, in teoria, dovrei cercare di superare
1) Il limite della tabella (mi hanno suggerito il FireBird ... su forgeNet
ho trovato qualche cosa .... ma non e' per delhi (credo))  cosi'  da poter
caricare i miei record
2) La velocita' ...... come fanno i DB con milioni di record ad essere
velocissimi ????
Spero in una vostro aiuto ... perche' ormai sono alla frutta ....
Grazie
Daniele
PS : Sull'inserimento ho tolto il locate ... non aveva troppo senso e i
tempi si sono supervelocizzati
       Per caricare 314.000 record occorrono solo 150 secondi !!!!!
      AIUTO per quanto sopra !!!


314.000 record in 150 secondi sono 180 milioni  record/giorno. Mica
poco.


2) La velocita' ...... come fanno i DB con milioni di record ad essere
Entrambe il limiti vengono superati cambiando database, ad esempio passando a
Firebird come ti ha suggerito Mauro Falzari.
Il link corretto e': 
http://www.firebirdsql.it
Mi sembra che piu' ottimizzato di cosi' non possa essere il tuo codice, a
questo punto entrano in gioco i limiti strutturali della TTable.
A questo proposito, guarda cosa scrive Wayne Niddery, del TeamB (conosci il
TeamB ? 
http://info.borland.com/newsgroups/teamb/
 ):
" If you are using TTables (or TQuerys with poor or non-existent Where
clauses) then your app is going to be reading and caching all the data from
the tables in your application's memory. On very large tables this can take
awhile. Initially only the first record (or first n in the case of an
attached DBGrid) are fetched, but as soon as you do anything like
TTable.Last, or RecordCount, or use OnFilterRecord (or the Filter property
if using a TQuery) then all records are forced to be read. This uses up
network bandwidth if reading from another machine, and client-side memory."
La ttable, quindi, non va piu' bene per questa mole di dati. Preleva,
installa, impara e utilizza Firebird.
Un'altra cosa volevo chiederti: dici di usare Delphi 6.0, ma quale versione ?
La professional ? La Personal ?
Guarda che Borland ha rilasciato due update, siamo addirittura arrivati alla
terza per la RTL. 
Ciao!
-- 
Morde. 
(
mo...@despammed.com
)
M-List di icld: 
http://it.groups.yahoo.com/group/icld/

indicizzati.
Geniale spunto...!! Non mi ero accorto dei tempi biblici della locate!


 terza per la RTL.
Per quanto strano possa sembrare non lo sapevo.
Cerchero' di aggirnarmi al piu' presto.
Ciao
Daniele
PS : Ho scaricato due files ecco i loro nomi
       FirebirdSQL-1.5.0BetaJDK_1.4.zip
       (dentro e' un casino se e' quello giusto ha dei componenti per D6?)
       Firebird-1.5.0.2926_beta_4_win32.zip
       (puo' servire a qualche cosa ??)

Cerchero' di aggirnarmi al piu' presto.
http://www.borland.com/products/downloads/download_delphi.html
Nella tabella "Registered Users ", clicca su "updates" della riga di delphi 6
Name     Platform    Version Release Date 
-----------------------------------------
Updates  !  Windows  !  6 !   03/12/2003 
