Avatar billede kennethv Nybegynder
02. oktober 2006 - 13:57 Der er 48 kommentarer og
2 løsninger

Optimering af kode.

Hej eksperter.

Et lille spørgsmål som går ud på optimering af kode.

Jeg har lavet en service som holder øje med en folder om en fil er oprettet. Når det sker, indlæser den filen og behander den. Jeg har en access db med 3 felter. 2 TEXT og 1 MEMO. Indhold fil, som er en txt fil, bliver "smidt" ind i DATALOG/MEMO. Og ejer af filen bliver lagt ind i NAME/TEXT og navnet på filen bliver lagt ind i FILE/TEXT. Mit problem er i starten når jeg oprettet records i db gik det bare hurtigt, nu efter af antalet af records er kommet op på 12000 går det meget langsomt. Er det fordi, at jeg har lavet noget "dårligt" kode. :) Og kan man optimere det jeg har lavet eller er det bare access der er sådan.
Jeg har ca 7000 filer endnu, som jeg skal have oprettet i access.
Kan nogen sige mig om det kan optimeres?

Nedenstående kode er koden for når en fil er oprettet:

procedure TALBackupCheck.DirWatch1NewFileCreated(Sender: TObject;
  const FileName: string);
var tmp : string; // MemoryStream : TMemoryStream;

begin
  if pos('CDATABUP',Filename) <> 0 then
  begin
    if pos('Log',Filename) <> 0 then
    begin
      if pos('DKSOLT',Filename) or pos('DKSODT',Filename) <> 0 then
      begin
        repeat
          Sleep(100);
        until not isFileInUse(Filename);
        try
          StringList := TStringList.Create;
          StringList.LoadFromFile(FileName);
        except
          on EFopenError do
          begin
            LogMessage('Cannot open file: ' + filename,EVENTLOG_INFORMATION_TYPE);
          end;
        end;
        Tmp := StringList.Strings[2];
        if (POS('ROBOCOPY', Tmp) <> 0) and (POS('Version XP010', Tmp) <> 0) then
        begin
          beep;
          TmpUSERNAME := StringList.Strings[8];
          Delete(TmpUSERNAME,1,pos('\',TmpUSERNAME));
          Delete(TmpUSERNAME,1,pos('\',TmpUSERNAME));
          Delete(TmpUSERNAME,1,pos('\',TmpUSERNAME));
          Delete(TmpUSERNAME,1,pos('\',TmpUSERNAME));
          TmpUSERNAME := copy(TmpUSERNAME,1,pos('\',TmpUSERNAME)-1);
          TmpFILE := FILENAME;
          Delete(TmpFILE,1,pos('\',TmpFILE));
          Delete(TmpFILE,1,pos('\',TmpFILE));
          Delete(TmpFILE,1,pos('\',TmpFILE));
          Delete(TmpFILE,1,pos('\',TmpFILE));
          Delete(TmpFILE,1,pos('\',TmpFILE));
          ADOTable1.Open;
          if not (ADOTable1.State in [dsInsert, dsEdit]) then
            ADOTable1.Insert;
          ADOTable1Name.AsString := TmpUSERNAME;
          ADOTable1FILE.AsString := TmpFILE;
          ADOTable1DataLog.LoadFromFile(Filename);
          ADOTable1.Post;
          ADOTable1.Close;
          DeleteFile(Filename);
        end;
      end;
      StringList.Free;
    end;
  end;
end;

PFT
Avatar billede vallemanden Nybegynder
02. oktober 2006 - 14:03 #1
min første tanke er '12000 records i access, tjaa så skal det gå langsomt'
Avatar billede kennethv Nybegynder
02. oktober 2006 - 14:16 #2
Men det burde den da kunne klare, ikk!
Avatar billede dkn Nybegynder
02. oktober 2006 - 15:56 #3
Prøv om du ikke kan nøjes med at kalde open når din service bliver started og så først lukke database forbindelsen igen når din service stopper.

Men ellers skulle den der funktion ikke være dem som tog mest tid, det er først når du vil til at søge i det antal records og det gør du vel et sted.
Avatar billede kennethv Nybegynder
02. oktober 2006 - 16:04 #4
Næ det gør jeg ikke. Jeg smider det bare ind og regner med at access klare det.

Jeg får problemer med at kalde open når min service starter. Den starter ikke hvis jeg gør det.
Avatar billede vallemanden Nybegynder
02. oktober 2006 - 18:11 #5
prøv at lave din ADOTable1.Open; på form create og ADOTable1.Close; når du lukker formen
Avatar billede martinlind Nybegynder
02. oktober 2006 - 21:22 #6
GLEM ALT OM ACCESS, den kører langsomt og bliver KUN langsomere jo mere du "putter" i din DB, også selv om du sletter igen og fylder nyt i.

Vil foreslå dig at bruge Interbase/Firebird ( www.firebirdsql.org ) Firebird er gratis og kører rigtig godt, du kan fint bruge Delphi's Interbase DB Componenter til den, og den er ca. en faktor 10-50 gange hurtigere.
Avatar billede kennethv Nybegynder
03. oktober 2006 - 08:37 #7
Hvad med SQL 2005 Express Edition? På længere sigt skal det kører på en SQL server aligevel.
Avatar billede martinlind Nybegynder
03. oktober 2006 - 09:21 #8
Sikkert også en mulighed, men den kender jeg ikke, men Access er og bliver en "køkkenbords" DB
Avatar billede kennethv Nybegynder
03. oktober 2006 - 09:26 #9
:)
Avatar billede hrc Mester
04. oktober 2006 - 13:34 #10
Brug en rigtig database, brug sql og brug transaktionsstyring:

  ADOQuery.Connection.BeginTrans;
  try

    Placér løkken her som indsætter 12000 records i tabellen

    ADOQuery.Connection.CommitTrans;
  except
    ADOQuery.Connection.RollbackTrans;
    raise;
  end;

Det kører mange gange hurtigere end hvis du "committer" hver enkelt indsættelse i tabellen (som den gør automatisk).

Prøv i øvrigt med transaktionsstyring på din access. Det kan være det sætter skub i det - men access er nutidens "paradox"-database så jeg tvivler.
Avatar billede martinlind Nybegynder
04. oktober 2006 - 14:05 #11
hrc >> Brug en rigtig database, brug sql og brug transaktionsstyring: <<

Det er det Firebird er/kan/har :-)
Avatar billede hrc Mester
04. oktober 2006 - 15:16 #12
Jep
Avatar billede kennethv Nybegynder
04. oktober 2006 - 15:35 #13
Nu har jeg installeret SQL 2005 EE det hjalp lidt, men hvis jeg skal gøre det du har nævnt, hvad er det jeg præcis skal putte ind som indsætter 12000 records i min tabel?

Er det sådan her:
ADOTable1.Connection.BeginTrans;
try
  ADOTable1.Open;
  if not (ADOTable1.State in [dsInsert, dsEdit]) then
    ADOTable1.Insert;
  ADOTable1Name.AsString := TmpUSERNAME;
  ADOTable1FILE.AsString := TmpFILE;
  ADOTable1DataLog.LoadFromFile(Filename);
  ADOTable1.Post;
  ADOTable1.Close;
  ADOTable1.Connection.CommitTrans;
except
  ADOTable1.Connection.RollbackTrans;
  raise;
end;

Den fejler ihvertfald ikke, men det er ikke fordi at der er nogen foreskel om jeg bruger den metode eller ikke. Men det går hurtigere nu efter at jeg bruger SQL 2005 EE.

HRC læg lige et svar og jeg deler point.

Er ADOQuery bedere end ADOTabel? Kan man det hele med ADOQuery som man kan med ADOTable og lidt til?
Avatar billede martinlind Nybegynder
04. oktober 2006 - 18:01 #14
Det med at commit'e uden om din løkke det kan spare tid hvis du indsætter "store klumper" / mange records på en gang, ellers tager det bare den tid det tager og er noget du skal gøre, hvis du bruger en trans. SQL db, som du jo gør nu :), men jeg vil foreslå dig at skrive din kode om til et "rigtig" insert into statement, en table har aldrig være hurtig når vi snakker sql servere, de er som navnet siger beregnet til SQL :)
Avatar billede martinlind Nybegynder
04. oktober 2006 - 18:02 #15
brug en ADOQuery i stedet for ADOTable
Avatar billede hrc Mester
04. oktober 2006 - 18:39 #16
Nu er det også et spørgsmål om religion om man bruger en TTable eller en TQuery. En TTable er generelt tungere i kommunikationen frem og tilbage til databasen. Desuden så er min "religion" at bruge SQL. Tror i øvrigt nok at det er hvad der sker i baggrunden når man bruger en TTable-afart.

I øvrigt. Du behøver ikke lukke/åbne tabellen efter du har indsat. Det sløver i alt fald også. Hold den åben mens du arbejder.
Avatar billede martinlind Nybegynder
04. oktober 2006 - 19:29 #17
åbne/lukke er ofte nødvendigt for at få opdateret sin table. Det er ikke et spm. hvis man vil ha' hurtig kode, så skal du kun bruge TTable til dine DB-grids, ellers KUN TQuery :)
Avatar billede kennethv Nybegynder
04. oktober 2006 - 21:23 #18
Jeg gør gerne et forsøg med at bruge ADOQuery, men jeg er nok bange for at I lige må hjælpe mig, hvordan man putter det i mine felter.
Avatar billede hrc Mester
05. oktober 2006 - 08:51 #19
Det er nu ret simpelt. Kommandoen for at indsætte records i en tabel er:

  INSERT INTO <tabel> (<feltnavn>,<feltnavn>,,,) VALUES(<:param>,<:param>,,,)

For en postnummertabel:

  INSERT INTO post
    (postnr, postby, postgade, postfirma, postland)
    VALUES(:postnr, :postby, :postgade, :postfirma, :postland)

Smæk en TADOQuery på din form / datamodul og forbind den til databasen. I din uses-sektion tilføjer du også lige "Variants"-unitten. Åbn sciptvinduet under komponentens SQL-property og indtast dit script. De forskellige parametre i VALUES() skal derefter sættes op. Klik på params og sæt datatypen på hver enkelt parameter. Sæt evt en default værdi ind.

I din kode skal du indsætte data i parametrene før du sender den afsted:

  Query.Parameters.ParamByName('postnr').Value := '5000';
  Query.Parameters.ParamByName('postby').Value := 'Odense C';

Hvis du har en værdi der er udefineret så er den Null. Det er derfor vi inkluderede Variants i vores uses-liste.

  Query.Parameters.ParamByName('postgade').Value := Null;

Så er vi klar til at indsætte recorden:

  Query.ExecSQL;

Nedenstående eksempel sætter de danske postnumre ind

  // Start transaktionen så alle ændringer gemmes i en buffer på DB-serveren
  Query.Connection.BeginTrans;
  try
    for i := 0 to Liste.Count do
    begin
      PostData := Liste[i]; // Type: TPostnrData
      Query.Parameters.ParamByName('postnr').Value := PostData.Postnr;
      Query.Parameters.ParamByName('postby').Value := PostData.Postby;
      Query.Parameters.ParamByName('postgade').Value := Null;
      Query.Parameters.ParamByName('postfirma').Value := Null;
      Query.Parameters.ParamByName('postland').Value := PostData.Postland; // Danmark
      Query.ExecSQL;
    end;
    // OK, Det hele er på serveren. Gem hele skidtet så andre brugere kan se det
    Query.Connection.CommitTrans;
  except
    // Ikke OK! Fang fejlen og rul operationen tilbage igen
    Query.Connection.RollbackTrans;

    // Send fejlen videre til programmet da brugeren skal informeres.
    raise;
  end;

// Hvis du er i tvivl om hvad "Liste" er, så har jeg ridset den og dens dataklasse op nedenfor:

TPostnrData = class
private
  fPostnr : string;
  fPostby : string;
  fPostland : integer;
public
  constructor Create(const aPostnr, aPostby : string; const aPostland : integer);
  property Postnr : string read fPostnr;
  property Postby : string read fPostby;
  property Postland : integer read fPostland;
end;

constructor TPostnrData.Create(const aPostnr, aPostby : string; const aPostland : integer);
begin
  inherited;
  fPostnr := aPostnr;
  fPostby := aPostby;
  fPostland := aPostland;
end;

TPostnrList = class(TObjectList)
private
  function GetPostnrData(const aIndex : integer) : TPostnrData;
public
  property Items[const aIndex : integer] : TPostnrData read GetPostnrData; default;
end;

function TPostnrList.GetPostnrData(const aIndex : integer) : TPostnrData;
begin
  result := TPostnrData(inherited Items[aIndex]);
end;
Avatar billede kennethv Nybegynder
05. oktober 2006 - 09:54 #20
Holda op. Jeg troede bare at man kunne gøre sådanne her:

ADOQuery1.Connection.BeginTrans;
try
  ADOQuery1.Open;
  if not (ADOQuery1.State in [dsInsert, dsEdit]) then
    ADOQuery1.Insert;
  ADOQuery1.FieldByName('NAME').AsString := TmpUSERNAME;
  ADOQuery1.FieldByName('FILE').AsString := TmpFILE;
  ADOQuery1.Post;
  ADOQuery1.Close;
  ADOQuery1.Connection.CommitTrans;
except
  ADOQuery1.Connection.RollbackTrans;
  raise;
end;
Avatar billede martinlind Nybegynder
05. oktober 2006 - 10:11 #21
NEJ, hvis du vil køre på en rigtig SQL-DB så skal du også lave rigtig SQL for at indsætte osv., men det er ikke så slemt som det ser ud til, du lærer det ret hurtigt så du kan komme vidre....
Avatar billede kennethv Nybegynder
05. oktober 2006 - 10:19 #22
ok. Tak :)
Avatar billede kennethv Nybegynder
05. oktober 2006 - 10:23 #23
Vil det sige, i det eksempel der nu er lavet, kan det bruges til at indsætte en hel txtfil i et felt?
Avatar billede hrc Mester
05. oktober 2006 - 11:19 #24
Ja, Det skulle kunne virke. Ville nok ændre dataobjektet så den tog en tekststreng som argument i constructoren. Den skulle så sørge for at fiske de enkelte elementer ud af linien og placere dem i de rette properties som efterfølgende kunne skrives.
Laver endnu et eksempel - og det kommer som skidt fra en spædekalv...

TFileData = class
public
  constructor Create(const aLine : string);
  property ...
  procedure SaveRecord(aQuery : TADOQuery);
end;

TFileList = class(TObjectList)
public
  constructor Create(const aFIlename : string);
  property Items[const aIndex : integer] : TFileData read GetFileData; default;
  procedure SaveRecords;
end;

{ TFileData }

constructor TFileData.Create(aLine : string);
var
  FieldNo, p : integer;
begin
  inherited;
  FieldNo := 0;

  // Initialiser felter før man parser strengen
  fUsername := '';
  fFilename := '';
  repeat
    p := pos(';',aLine); // Antager her at felter adskilt af semikolon
    if p > 0 then
    begin
      inc(FieldNo); // Felttæller
      case FieldNo of
        1 : fNavn := copy(aLine,1,p-1);
        2 : ....
      end;
      System.Delete(aLine,1,p);
    end;
  until p = 0;
  fSidsteFelt := aLine; // Skal huske det sidste felt
end;

procedure TFileData.SaveRecord(aQuery : TADOQuery);
var
  LocalTransaction : boolean;
begin
  // Hvis den ikke kaldes i en insert-løkke så lav transaktionen selv
  LocalTransaction := not aQuery.InTransaction;

  if LocalTransaction then Query.Connection.BeginTrans;
  try
    aQuery.Parameters.ParamByName('').Value := fNavn;
    aQuery.Parameters.ParamByName('').Value := ..
    aQuery.Parameters.ParamByName('').Value := ..
    aQuery.ExeSql;

    if LocalTransaction then Query.Connection.CommitTrans;
  except
    if LocalTransaction Query.Connection.RollbackTrans;
    raise;
  end;
end;

{ TFileList }

constructor TFileList.Create(const aFilename : string);
var
  i : integer;
  Lines : TStringList;
begin
  // En eller andet kontrol om filen er lukket eller i brug

  Lines := TStringList.Create;
  try
    Lines.LoadFromFile(aFilename);
    for i := 0 to Lines.Count - 1 do
      Add(TFileData(Lines[i]));
  finally
    Lines.Free;
  end;
end;

procedure TFileList.SaveRecords;
var
  i : integer;
  Query : TADOQuery;
begin
  Query := TADOQuery.Create(nil);
  try
    Query.Connection := Din_ADOConnection;
    Query.SQL.Text :=
      'INSERT INTO ... ';
    Query.Connection.BeginTrans;
    try
      for i := 0 to Count - 1 do
        Items[i].SaveRecord(Query);
      Query.Connection.CommitTrans;
    except
      Query.Connection.RollbackTrans;
      raise
    end;
  finally
    Query.Free;
  end;
end;
Avatar billede kennethv Nybegynder
05. oktober 2006 - 12:50 #25
Jeg vil helt klart lure det her af.
Kan du hjælpe mig med denne opgave:

http://www.eksperten.dk/spm/735268
Avatar billede kennethv Nybegynder
05. oktober 2006 - 13:25 #26
Er klaret.
Avatar billede kennethv Nybegynder
06. oktober 2006 - 12:17 #27
HRC: Du hjulpet mig med opgaven tidligere i denne tråd:
http://www.eksperten.dk/spm/721758
Og det med at smide hele txtfil i en memo i en access fungeret skide godt, men kan man ikke bruge det samme hvis man skulle smide 2 felter mere ind? Jeg her prøvet dette, men det spiller ikke:

          ADOQuery1.SQL.Text := 'Insert Into DATALOG (NAME, FILENAME) Values(:TmpUSERNAME,:TmpFILE)';
          ADOQuery1.Connection.BeginTrans;
          try
{            ADOTable1.Open;
            if not (ADOTable1.State in [dsInsert, dsEdit]) then
              ADOTable1.Insert;
            ADOTable1Name.AsString := TmpUSERNAME;
            ADOTable1FILENAME.AsString := TmpFILE;
            ADOTable1DataLog.LoadFromFile(Filename);
            ADOTable1.Post;
            ADOTable1.Close;}
            ADOQuery1.Parameters.ParamByName('NAME').Value := TmpUSERNAME;
            ADOQuery1.Parameters.ParamByName('FILENAME').Value := TmpFILE;
            MemoryStream.LoadFromFile(FILENAME);
            MemorySTream.Seek(0,soFromBeginning);
            ADOQuery1.Parameters.ParamByName('DATALOG').LoadFromStream(MemoryStream,ftFmtMemo);
            ADOQuery1.ExecSQL;
            ADOQuery1.Connection.CommitTrans;
          except
            ADOQuery1.Connection.RollbackTrans;
            Raise;
          end;
Avatar billede hrc Mester
07. oktober 2006 - 14:01 #28
prøv dette her:

Du havde mistofstået brugen af insert-kommandoen. Det første parantessæt er de felter du vil indsætte noget i. Det andet er der hvor værdierne placeres - og her har vi smidt nogle parametre som vi efterfølgende fylder noget i.

Jeg har ikke testet nedenstående, men det er i alt fald skridt i den rigtige retning.

  ADOQuery1.SQL.Text :=
    'Insert Into DATALOG (NAME, FILENAME, DATALOG) Values(:ParamNAME,:ParamFILE,:ParamDATALOG)';
  ADOQuery1.Connection.BeginTrans;
  try
    ADOQuery1.Parameters.ParamByName('ParamNAME').Value := TmpUSERNAME;
    ADOQuery1.Parameters.ParamByName('ParamFILE').Value := TmpFILE;
    MemoryStream.LoadFromFile(FILENAME);
    MemorySTream.Seek(0,soFromBeginning);
    ADOQuery1.Parameters.ParamByName('ParamDATALOG').LoadFromStream(MemoryStream,ftFmtMemo);
    ADOQuery1.ExecSQL;
    ADOQuery1.Connection.CommitTrans;
  except
    ADOQuery1.Connection.RollbackTrans;
    Raise;
  end;
Avatar billede kennethv Nybegynder
08. oktober 2006 - 11:19 #29
Næ, det virker ikke. :)
Jeg får denne fejl i min eventlog:
The following information is part of the event: Access violation at address 0048D555 in module 'BackupCheck.exe'

Nu lige for at få Kong Hans her til bare at forstå lidt af det hele.
Det du lige har give et eksempel på, erstatter det hele den smøre du lavet før? Går det hurtigere med hele "smøren" eller er det, det samme?
Avatar billede martinlind Nybegynder
08. oktober 2006 - 12:17 #30
Kong Hans >>

'Insert Into <TabelNavn> (<FeltNavne [felt1,felt2,felt3] >) Values(<Feltværdi> [felt1,felt2,felt3] )'

Så det vi kan se, er at den SQL-sætning indsætter en række i din tabel :)

  ADOQuery1.Connection.BeginTrans;
  try
    ADOQuery1.Parameters.ParamByName('Felt1').Value := felt1-værdi;
    ADOQuery1.Parameters.ParamByName('felt2').Value := felt2-værdi;
    MemoryStream.LoadFromFile('MyFile.txt');
    MemorySTream.Seek(0,soFromBeginning);
    // felt3-værdi er her en filestream
    ADOQuery1.Parameters.ParamByName('felt3').LoadFromStream(MemoryStream,ftFmtMemo);
    ADOQuery1.ExecSQL; // udfør sql-sætning
    ADOQuery1.Connection.CommitTrans;
  except
    ADOQuery1.Connection.RollbackTrans;
    Raise;
  end;

Lettede det forståelsen ??

Access violation at address 0048D555 in module 'BackupCheck.exe' >> Betyder som regl at du bruger et Object/Classe du ikke har oprettet ( MyObj := TMyObj.Create )
Avatar billede kennethv Nybegynder
08. oktober 2006 - 12:27 #31
:)

Jeg mener selv at jeg gjort fuldstændig som du bad om, så jeg lidt svært at se hvor jeg skulle have gjort noget galt. Det her er min procedure:

procedure TALBackupCheck.DirWatch1NewFileCreated(Sender: TObject;
  const FileName: string);
var tmp : string;  MemoryStream : TMemoryStream;
begin
  MemoryStream := TMemoryStream.Create;
  LogMessage('File created: ' + FileName,EVENTLOG_AUDIT_SUCCESS);
  if pos('CDATABUP',Filename) <> 0 then
  begin
    if pos('Log',Filename) <> 0 then
    begin
      if pos('DKSOLT',Filename) or pos('DKSODT',Filename) <> 0 then
      begin
        repeat
          Sleep(100);
        until not FileInUse(Filename);
        try
          StringList := TStringList.Create;
          StringList.LoadFromFile(FileName);
        except
          on EFopenError do
          begin
            LogMessage('Cannot open file: ' + filename,EVENTLOG_INFORMATION_TYPE);
          end;
        end;
        Tmp := StringList.Strings[2];
        if (POS('ROBOCOPY', Tmp) <> 0) and (POS('Version XP010', Tmp) <> 0) then
        begin
          beep;
          Navn := StringList.Strings[8];
          Delete(Navn,1,pos('\',Navn));
          Delete(Navn,1,pos('\',Navn));
          Delete(Navn,1,pos('\',Navn));
          Delete(Navn,1,pos('\',Navn));
          Navn := copy(Navn,1,pos('\',Navn)-1);
          FilNavn := FILENAME;
          Delete(FilNavn,1,pos('\',FilNavn));
          Delete(FilNavn,1,pos('\',FilNavn));
          Delete(FilNavn,1,pos('\',FilNavn));
          Delete(FilNavn,1,pos('\',FilNavn));
          Delete(FilNavn,1,pos('\',FilNavn));
          ADOQuery1.SQL.Text := 'Insert Into DATALOG (NAME, FILENAME, DATALOG) Values(:ParamNAME,:ParamFILE,:ParamDATALOG)';
          ADOQuery1.Connection.BeginTrans;
          try
            ADOQuery1.Parameters.ParamByName('ParamNAME').Value := Navn;
            ADOQuery1.Parameters.ParamByName('ParamFILE').Value := Filnavn;
            MemoryStream.LoadFromFile(FILENAME);
            MemorySTream.Seek(0,soFromBeginning);
            ADOQuery1.Parameters.ParamByName('ParamDATALOG').LoadFromStream(MemoryStream,ftFmtMemo);
            ADOQuery1.ExecSQL;
            ADOQuery1.Connection.CommitTrans;
          except
            ADOQuery1.Connection.RollbackTrans;
            Raise;
          end;
          DeleteFile(Filename);
        end;
      end;
      MemoryStream.Free;
      StringList.Free;
    end;
  end;
end;
Avatar billede martinlind Nybegynder
08. oktober 2006 - 12:45 #32
procedure TALBackupCheck.DirWatch1NewFileCreated(Sender: TObject;
  const FileName: string);
var tmp : string;  MemoryStream : TMemoryStream;
begin
  MemoryStream := TMemoryStream.Create;
  LogMessage('File created: ' + FileName,EVENTLOG_AUDIT_SUCCESS);
  if pos('CDATABUP',Filename) <> 0 then
  begin
    if pos('Log',Filename) <> 0 then
    begin
      if pos('DKSOLT',Filename) or pos('DKSODT',Filename) <> 0 then
      begin
        repeat
          Sleep(100);
        until not FileInUse(Filename);
        try
          StringList := TStringList.Create;
          StringList.LoadFromFile(FileName);
        except
          on EFopenError do
          begin
            LogMessage('Cannot open file: ' + filename,EVENTLOG_INFORMATION_TYPE);
        end;
        end;
        // Det kunne være her den fejler for du glemmer at checke om du har loadet en fil
        // og det fortsætter du med længere nede
        Tmp := StringList.Strings[2];
        if (POS('ROBOCOPY', Tmp) <> 0) and (POS('Version XP010', Tmp) <> 0) then
        begin
          beep;
          Navn := StringList.Strings[8];
          Delete(Navn,1,pos('\',Navn));
          Delete(Navn,1,pos('\',Navn));
          Delete(Navn,1,pos('\',Navn));
          Delete(Navn,1,pos('\',Navn));
          Navn := copy(Navn,1,pos('\',Navn)-1);
          FilNavn := FILENAME;
          Delete(FilNavn,1,pos('\',FilNavn));
          Delete(FilNavn,1,pos('\',FilNavn));
          Delete(FilNavn,1,pos('\',FilNavn));
          Delete(FilNavn,1,pos('\',FilNavn));
          Delete(FilNavn,1,pos('\',FilNavn));
          ADOQuery1.SQL.Text := 'Insert Into DATALOG (NAME, FILENAME, DATALOG) Values(:ParamNAME,:ParamFILE,:ParamDATALOG)';
          ADOQuery1.Connection.BeginTrans;
          try
            ADOQuery1.Parameters.ParamByName('ParamNAME').Value := Navn;
            ADOQuery1.Parameters.ParamByName('ParamFILE').Value := Filnavn;
            MemoryStream.LoadFromFile(FILENAME);
            MemorySTream.Seek(0,soFromBeginning);
            ADOQuery1.Parameters.ParamByName('ParamDATALOG').LoadFromStream(MemoryStream,ftFmtMemo);
            ADOQuery1.ExecSQL;
            ADOQuery1.Connection.CommitTrans;
          except
            ADOQuery1.Connection.RollbackTrans;
            Raise;
          end;
          DeleteFile(Filename);
        end;
      end;
      MemoryStream.Free;
      StringList.Free;
    end;
  end;
end;
Avatar billede martinlind Nybegynder
08. oktober 2006 - 12:46 #33
kig forøvrigt lidt på ExtractFileName(), ExtractFilePath() funktionerne, så kunne du måske slippe for dine delete()
Avatar billede kennethv Nybegynder
08. oktober 2006 - 23:11 #34
Tak for tippet angående ExtractFilename() og ExtraFilePath(). Det kunne jeg bruge. Men det andet forstår jeg ikke helt. Jeg går helt bestemt udfra at hvis der var en fejl i load af fil at jeg får en besked i min eventlog.

Hvis jeg bruger denne løsning har jeg ingen problemer.

procedure TALBackupCheck.DirWatch1NewFileCreated(Sender: TObject;
  const FileName: string);
var tmp : string;  MemoryStream : TMemoryStream;
begin
  MemoryStream := TMemoryStream.Create;
  LogMessage('File created: ' + Filename,EVENTLOG_AUDIT_SUCCESS);
  if pos('CDATABUP',Filename) <> 0 then
  begin
    if pos('Log',Filename) <> 0 then
    begin
      if pos('DKSOLT',Filename) or pos('DKSODT',Filename) <> 0 then
      begin
        repeat
          Sleep(100);
        until not FileInUse(Filename);
        try
          StringList := TStringList.Create;
          StringList.LoadFromFile(FileName);
        except
          on EFopenError do
          begin
            LogMessage('Cannot open file: ' + filename,EVENTLOG_INFORMATION_TYPE);
          end;
        end;
        Tmp := StringList.Strings[2];
        if (POS('ROBOCOPY', Tmp) <> 0) and (POS('Version XP010', Tmp) <> 0) then
        begin
          beep;
          Navn := StringList.Strings[8];
          Delete(Navn,1,pos('\',Navn));
          Delete(Navn,1,pos('\',Navn));
          Delete(Navn,1,pos('\',Navn));
          Delete(Navn,1,pos('\',Navn));
          Navn := copy(Navn,1,pos('\',Navn)-1);
          Filnavn := ExtractFileName(Filename);
          ADOTable1.Open;
          if not (ADOTable1.State in [dsInsert, dsEdit]) then
            ADOTable1.Insert;
          try
            ADOTable1Name.AsString := Navn;
            ADOTable1FILENAME.AsString := FilNavn;
            ADOTable1DataLog.LoadFromFile(Filename);
          finally
            ADOTable1.Post;
            ADOTable1.Close;
          end;
          DeleteFile(Filename);
        end;
      end;
      MemoryStream.Free;
      StringList.Free;
    end;
  end;
end;
Avatar billede martinlind Nybegynder
09. oktober 2006 - 13:17 #35
Det gør du også, men fordi du har except så får du behandlet færdig der, og prgrammet kører vidre, der efter prøver du at læse line 2 i din ( ikke indlæste fil ) og det samme gør du i line 8, det kunne godt være en årsag til at du får din "Access Violation"

Tmp := StringList.Strings[2]; <---- hvis der ikke var en fil eller en openerror så læser du bare uden at checke !!
Avatar billede martinlind Nybegynder
09. oktober 2006 - 13:18 #36
hvis du stadig ikke forstår, så prøv at læs lidt på

try
finally
end;

og på

try
except
end;

/Martin
Avatar billede kennethv Nybegynder
09. oktober 2006 - 22:54 #37
Men jeg ved jo at filen på det tidspunkt findes og hvis ikke der er nogen problemer i at åbne den, så jeg kan da vel godt lave denne:
Tmp := StringList.Strings[2];

Men jeg har set på dit forslag og spørger ganske forsigtig om det skal gøres på denne måde. Jeg er lidt usikker på om begge metoder skal bruges, men jeg prøvet dette:
        try
          StringList := TStringList.Create;
          try
            StringList.LoadFromFile(FileName);
          except
            on EFopenError do
            begin
              LogMessage('Cannot open file: ' + filename,EVENTLOG_INFORMATION_TYPE);
            end;
          end;
        finally
          Tmp := StringList.Strings[2];
        end;

Hvis jeg skal bruge:
try
finally

kan jeg ikke sådan lige se hvor exception kommer ind i billedet.
Avatar billede martinlind Nybegynder
10. oktober 2006 - 09:06 #38
det der er problemmet er at du "kører" vidre i din kode fordi du har en try except, så du bør nok tænke din struktur om, for hvis filen ikke findes er StringList.count = 0 og så vil du få fejl ved at prøve en Strings[2]

Du kunne overveje ot checke med if FileExists() then .lav din db indsæt .... else ..lav ikke noget ( vent )..
Avatar billede kennethv Nybegynder
10. oktober 2006 - 09:31 #39
Ok, men selve proceduren hedder "NewFileCreated" så burde den ikke findes på det tidpunkt?
procedure TALBackupCheck.DirWatch1NewFileCreated(Sender: TObject;
  const FileName: string);

Og i det at jeg tjekker for om den rigtig sti er der, med:
  if pos('CDATABUP',Filename) <> 0 then
  begin
    if pos('Log',Filename) <> 0 then
    begin
      if pos('DKSOLT',Filename) or pos('DKSODT',Filename) <> 0 then

så vil jeg da gå udfra filen er der, for ellers ville jeg ikke komme så "langt" i koden?

Og som jeg nævnte, at når jeg bruger ADOTable har jeg ingen problemer. :) Problemet opstår først når jeg bruger ADOQuery.
Avatar billede kennethv Nybegynder
10. oktober 2006 - 09:33 #40
Hov, iøvrigt. Hvor kommer
try
finally
end

ind henne, når du lige pludselig slår over i at jeg skal kigge på fileexists?

:)
Avatar billede martinlind Nybegynder
10. oktober 2006 - 09:42 #41
Jo det lige netop det du gør, try except fanger jo fejlen og behandler den og så kører din kode vidre, det er det der er dit problem !!!

PS. mht. til kode og det med at gå ud fra, det kan du godt glwemme, du kan ikke gå udfra noget :)
Avatar billede martinlind Nybegynder
10. oktober 2006 - 09:43 #42
Er du i tvivl om mere, så smut ned på bib. og læs i nogle pacal/delphi bøger, det er lidt af det grundlægende du ikke helt har på plads lyder det som om
Avatar billede kennethv Nybegynder
10. oktober 2006 - 10:02 #43
:) De findes ikke mere og bliver heller ikke lavet mere.
Avatar billede kennethv Nybegynder
10. oktober 2006 - 10:17 #44
Men det jeg havde forsøgt før med try..finally og try..except er det sådan lidt på rette vej?
Avatar billede kennethv Nybegynder
10. oktober 2006 - 10:28 #45
Nu kommer der jo også mange N00B spørgsmål her, så vær nu lige venlig at bære over med mig. :)

Jeg har prøvet dette:
procedure TALBackupCheck.DirWatch1NewFileCreated(Sender: TObject;
  const FileName: string);
var tmp : string;  MemoryStream : TMemoryStream;
begin
  MemoryStream := TMemoryStream.Create;
  LogMessage('File created: ' + Filename,EVENTLOG_AUDIT_SUCCESS);
  if pos('CDATABUP',Filename) <> 0 then
  begin
    if pos('Log',Filename) <> 0 then
    begin
      if pos('DKSOLT',Filename) or pos('DKSODT',Filename) <> 0 then
      begin
        repeat
          Sleep(100);
        until not FileInUse(Filename);
        if FileExists(FileName) then
        begin
          StringList := TStringList.Create;
          try
            StringList.LoadFromFile(Filename);
            if (POS('ROBOCOPY', StringList.Strings[2]) <> 0) and (POS('Version XP010', StringList.Strings[2]) <> 0) then
            begin
              beep;
              Navn := StringList.Strings[8];
              Delete(Navn,1,pos('\',Navn));
              Delete(Navn,1,pos('\',Navn));
              Delete(Navn,1,pos('\',Navn));
              Delete(Navn,1,pos('\',Navn));
              Navn := copy(Navn,1,pos('\',Navn)-1);
              LogMessage(Navn,EVENTLOG_INFORMATION_TYPE);
              Filnavn := ExtractFileName(Filename);
              LogMessage(FilNavn,EVENTLOG_INFORMATION_TYPE);
              ADOQuery1.SQL.Text := 'Insert Into DATALOG (NAME, FILENAME, DATALOG) Values(:ParamNAME,:ParamFILE,:ParamDATALOG)';
              ADOQuery1.Connection.BeginTrans;
              try
                ADOQuery1.Parameters.ParamByName('ParamNAME').Value := Navn;
                ADOQuery1.Parameters.ParamByName('ParamFILE').Value := Filnavn;
                MemoryStream.LoadFromFile(FILENAME);
                MemorySTream.Seek(0,soFromBeginning);
                ADOQuery1.Parameters.ParamByName('ParamDATALOG').LoadFromStream(MemoryStream,ftFmtMemo);
                ADOQuery1.ExecSQL;
                ADOQuery1.Connection.CommitTrans;
              except
                ADOQuery1.Connection.RollbackTrans;
                Raise;
              end;
            end;
          finally
            MemoryStream.Free;
            StringList.Free;
          end;
        end;
      end;
    end;
  end;
end;

Men får nnu denne fejl i min eventlog:

Operand type clash: image is incompatible with nvarchar(max).
Avatar billede martinlind Nybegynder
10. oktober 2006 - 13:31 #46
Det ved jeg ikke lige hvorfor du fåe den fejl.

Meeen det med delphi børgene, mon ikke du stadig kan låne dem på bib. det tror jeg.

Ellers så prøv at læs lidt i delphi online hjælp omkring Try finally og try except

det du skal tænke på er at hvis du "selv fanger" din fejl med en try except så kører programet vidre, som om der ikke har været en fejl, for den har du jo lige fanget :)
Avatar billede hrc Mester
10. oktober 2006 - 13:44 #47
nvarchar er begrænset til 8kb i MS-SQL - og sikkert også i Access. De skal jo ligne hinanden, også på de dumme ting. Mon ikke der er en datatype der hedder "image". Prøv den.
Avatar billede kennethv Nybegynder
10. oktober 2006 - 14:45 #48
MARTIN: Jeg har læst det online og har prøvet det du ser. :)

HRC: Men hvis jeg køre det via ADOTable har jeg ingen problemer. Jeg undersøger det lige.
Avatar billede kennethv Nybegynder
10. oktober 2006 - 14:50 #49
HRC: Image findes, men kan ikke vælge den da det ikke kan konverteres. :(
Avatar billede kennethv Nybegynder
17. oktober 2006 - 14:38 #50
Jeg er nok bange for at I må hjælpe mig hvad jeg kan gøre her. Hvorfor virker det med ADOTable og ikke ADOQuery?
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester

IT-JOB

AL Sydbank A/S (tidligere Arbejdernes Landsbank)

Teamleder til AL Sydbanks GDPR & Tech Regulation i Aabenraa

IT-Universitetet i København

CIO

Netcompany A/S

Test Consultant

Djurslands Bank

IT-udvikler