AW: Kundenliste
Ich bin da erst einmal ratlos. Man kann es drehen und wenden, die procedure SaveToDB stimmt irgenwie nicht. Weder mit
Delphi-Quellcode:
noch mit
for I:= Self.Count-1 downto 0 do
Delphi-Quellcode:
läuft die Sache rund.
for I:=0 to Self.count-1 do
Es ist doch nicht normal, dass ein Speichervorgang von ca. 1000 Datensätzen gut 5 Minuten dauert und im Ergebnis dann 2000 Datensätze vorhanden sind, obwohl überhaupt keine Veränderung in der Datenbank vorgenommen wurde. Das Hinzufügen einer Methode Speichern zum Objekt TCustumer würde nach meinem Verständnis bedeuten, dass ich die gleiche Procedure SaveToDB von TCustomerList hin zu TCustomer schreibe und dann aufrufen soll? Das wäre doch das gleiche in Grün. Möglicherweise habe ich ein Brett vor dem Kopf. Ich vermute, dass die SaveToDB-procedure eine Macke hat, egal ob
Delphi-Quellcode:
oder umgekehrt. Es ist doch nicht normal, dass ein solcher Vorgang gut 6 Minuten dauern soll.
for I:= 0 to self.count-1 do
Leider bin ich mit meinem Latein am Ende. |
AW: Kundenliste
Zitat:
Keine Veränderung in der Datenbank? also wurde nichts gespeichert und die 2000 Datensätze waren von Anfang an vorhanden? Was die Speicherdauer angeht, das kommt ganz auf den Server und die gespeicherte Datenmenge an, aber 5 Minuten ist schon recht happig. Gruß K-H |
AW: Kundenliste
Du hättest als erstes mal die Möglichkeit einen Haltepunkt am Anfang der SaveToDB-Routine zu setzen, und mit F8 mal schrittweise durchzusteppen. Dann schaust du dir erst mal den Wert von CustomerList.Count an, ob der überhaupt bei 2000 liegt, bzw. sich jedes mal verdoppelt.
Dann würde ich mal einen Blick auf die ID werfen. Wenn die >-1 ist, springt er auf das richtige SQL-Statement (Update)? Oder macht er womöglich immer ein insert? Du kannst auch mal folgendes Probieren. Setze eine Haltepunkt auf die Zeile
Delphi-Quellcode:
. Diese wird ja nur bei einem Insert aufgerufen. Dann Programm starten, DB Laden und direkt danach wieder speichern. Da kein Datensatz neu ist, dürfte ja kein Insert auftreten und die Zeile nie angesprochen werden. Sollte das doch der Fall sein, stimmt ja was mit der ID nicht.
zqryMain.Params.ParamValues['ID']:=self[i].ID;
Weiterhin könntest du noch mal die Routine LoadFromDB anschauen. Werden da wirklich vor dem Laden alle Object von der CustomerList gelöscht (CustomerList.Clear)? Wenn nicht, würde das bedeuten, dass er immer alles Datensätze beim laden anhängt, und dann hättest du nach dem Laden immer 1000 mehr (Sofern 1000 in der DB sind). Warum überhaupt gleich 1000 Datensätze zum Testen/Debuggen? Das macht das ganze doch eher unübersichtlich und langwierig. Fang mit einem Datensatz an. Laden, Editieren, Speicher und wieder Laden. Sind es dann 2? Auch das löschen und Hinzufügen zum testen wäre mit weniger Datensätzen schneller erledigt. Im Normalfall gilt, was mit 10 Datensätzen fehlerfrei läuft, läuft auch mit 1000 und 10000. Vielleicht baust du dir auch eine Statuszeile ein, in der die CustomerList.Count steht, und nach jeder Aktion aktualisiert wird. Aber damit du nicht ewig suchst, dein Fehler in der SaveToDB. Genauer beim zqryMain.sql.text:=. Wenn ID=-1 dann lädst du das Update und wenn ID>-1 dann verwendest du Insert. Das ist logischerweise verkehr herum. Beim Bestücken der Parameter machst du es dann aber wieder richtig. Aber du hast noch andere Fehler drin, die dir später noch viel Kopfzerbrechen bereiten werden. |
AW: Kundenliste
Ich habe mal ein bisschen rumgespielt, und mir mal eine Zeitmessung eingebaut.
Bei 47416 Datensätzen dauert das Laden aus der DB: 17 Sek. das Füllen des Listview: 232 Sek. das Speichern (mit deinem Fehler): 398 Sek. (weil ja fehlerhafterweise jeder Datensatz mit Insert neu eingefügt wird). Damit könnte keiner arbeiten. Selbst wenn du den Fehler beseitigst, wird die Zeit für das Speichern nicht so kleiner, dass es komfortabel wäre, denn es würde ja auch jeder Datensatz trotzdem gespeichert, nur halt mit Update. Abhilfe, bei jetzigem Programmaufbau, würde z.B. eine Hilfevariable im Customer-Object schaffen. Du könnte in der TCustomer-Klasse noch die Property Modified als Boolean einfügen, die im Constructor Create erst einmal auf False gesetzt wird. Wird nun ein Datensatz verändert, oder hinzugefügt, so wird CustomerList[x].Modified bzw. Customer.Modified auf True gesetzt. In der SaveToDB-Routine könntest du nun erst einmal auf Modified prüfen und so entscheiden, ob der Datensatz überhaupt gespeichert werden muss. So hättest du die Zeit für das Speichern auf wenige Sekunden oder Millisekunden reduziert. Darfst aber nicht vergessen, nach dem Speichern des Datensatzes Modified wieder auf False zu setzen. Sinnvoll wäre auch, nach dem editieren den zugehörigen ListView-Eintrag gleich zu aktualisieren, damit nach dem SaveToDB auch wirklich das richtige im Listview steht (ist natürlich suboptimal, aber bei jetzigem Programmaufbau am sinnvollsten). Ähnliches Problem besteht beim löschen eines Datensatzes. Zur Zeit löscht du ihn nur aus der CustomerList und machst anschließend ein FuelleListView. Damit ist er zwar aus der Liste, aber in der DB immer noch vorhanden und beim nächsten Laden wieder da. Außerdem dauert das FuelleListView bei 47416 Datensätzen ja fast 4 Minuten. Also inakzeptabel. Hier hättest du 2 Möglichkeiten, entweder du entfernst per SQL den Datensatz gleich aus der DB und den dazugehörigen Eintrag aus der ListView, oder du machst es wie oben mit einer Hilfsproperty, die z.B. Deleted heißen könnte. Auf die müsstest du dann aber in der SaveToDB auch eingehen, und ein passendes Delete-Statement für SQL bereitstellen. Was die Zeit angeht, die das Listview benötigt, bis alles Daten der CustomerList dort vorhanden ist, so wird man dort wohl keine signifikante Geschwindigkeitssteigerung erreichen. Zumindest nicht mit Möglichkeiten, die du jetzt hier auch verstehen würdest. Für diesen Fall würde sich aber ein VirtualStringTree gerade zu aufdrängen. Das ist zwar vom Handling her etwas komplizierter, aber wenn man die Arbeitsweise erst mal verstanden hat, schon fast genial. Und in deinem Fall gut einsetzbar, da du bereit die passenden Objecte vorliegen hast. Und für alle anderen, die das jetzt gelesen haben: Sicherlich gibt es wesentlich bessere Möglichkeiten das speichern der Daten so zu realisieren, damit viele der oben genannten Schritte gar nicht notwendig wären. Aber ich wollte EdAdvokat erst mal nur die Möglichkeiten und Probleme aufzeigen, ohne Ihn vollends zu verwirren. |
AW: Kundenliste
Vielen Dank für die vielen Hinweise, die ich z.T. jedoch noch nicht verarbeiten kann. Ich konzentriere mich zunächst darauf die SaveToDB-procedure ordentlich zum laufen zu bekommen und dann werde ich mich an die Hinweise zur Verbsserung des Programms machen. Meine Versuche mit dem Debugger zu arbeiten waren durchwachsen. Ich habe den Hinweis mit dem Haltepunkt beachtet und mit F8 Zeile für Zeile geprüft. Er durchlief tatsächlich nur die update-Routine und nicht Insert. Die Prüfung in der LoadFromDB ob clear tatsächlich keinen Datensatz mehr hat war für mich sehr unübersichtlich, denn ich weiss gar nicht was ich da wo suchen sollte.
Nun erst einmal zum Ergebnis SaveToDB hier die jetzige Fassung, hoffentlich richtig:
Delphi-Quellcode:
Damit kann ich einen hinzugefügten Datensatz(der als ID zunächst -1 hat) speichern und dann wieder laden und er hat dann eine ID der Datenbank, so wie es sein sollte. Auch das Bearbeiten eines bestehenden Datensatzes und das Speichern klappt. Nach Laden mit LoadFromDB ist der geänderte Datensatz sichtbar. Nur nach dem Löschen von Datensätzen und Speichern erscheint nach LoadFromDB oder Neustart des Programms wieder der gelöschte Datensatz. Das bekomme ich einfach nicht hin.
procedure TCustomerList.SavetoDB(con: TZConnection);
var zqryMain: TZQuery; i: Integer; begin zqryMain:=TZQuery.Create(nil); try zqryMain.connection:=con; for I:= 0 to self.Count-1 do begin if self[i].ID>-1 then zqryMain.SQL.Text:='UPDATE WARENVERKAUF1 SET KDNR=:KNR, NAME =:NAM, VORNAME=:VNA, FIRMA=:FIR, PRODUKT=:PRO, ANZAHL=:ANZ, PREIS=:PRE WHERE ID=:CID' else zqryMain.SQL.text:='INSERT INTO WARENVERKAUF1(KDNR,NAME,VORNAME,FIRMA,PRODUKT,ANZAHL,PREIS) VALUES(:KNR, :NAM, :VNA, :FIR, :PRO, :ANZ, :PRE)'; zqryMain.params.parseSQL(zqryMain.sql.text, True); if self[i].ID>-1 then begin zqryMain.Params.ParamValues['CID']:=self[i].ID; //update zqryMain.params.ParamValues['KNR']:=self[i].KDNR; zqryMain.params.paramValues['NAM']:=self[i].Name; zqryMain.params.paramValues['VNA']:=self[i].Vorname; zqryMain.params.ParamValues['FIR']:=self[i].Firma; zqryMain.Params.ParamValues['PRO']:=self[i].Produkt; zqryMain.Params.ParamValues['ANZ']:=self[i].Anzahl; zqryMain.params.paramValues['PRE']:=self[i].Preis; zqryMain.ExecSQL; end else begin zqryMain.params.ParamValues['KNR']:=self[i].KDNR; //insert zqryMain.params.paramValues['NAM']:=self[i].Name; zqryMain.params.paramValues['VNA']:=self[i].Vorname; zqryMain.params.ParamValues['FIR']:=self[i].Firma; zqryMain.Params.ParamValues['PRO']:=self[i].Produkt; zqryMain.Params.ParamValues['ANZ']:=self[i].Anzahl; zqryMain.params.paramValues['PRE']:=self[i].Preis; zqryMain.ExecSQL; end; end; finally zqryMain.Free; end; end; Ich hätte noch so viele Fragen, aber zunächst soll das Hauptproblem gelöst werden. Ist die Procedure wirklich richtig???? Im Hauptformular rufe ich mit dem Button löschen die Routine auf:
Delphi-Quellcode:
hier wird doch der ausgewählte Datensatz aus der objectlist gelöscht. Danach wird fülleListview aufgerufen. Das ist doch auch i.o.
CustomerList.Delete(lvCustomer.Selected.Index);
Wenn ich wüßte wie ich mit dem Debugger diese Problematik prüfen könnte. Da er ja tatsächlich auch sichtbar den Datensatz aus der Liste löscht, jedoch beim Speichern ihn wohl nicht gelöscht speichert, erscheint er danach wieder. |
AW: Kundenliste
Und wo löschst Du den Datensatz aus der Datenbank?
|
AW: Kundenliste
Zitat:
Wenn ich das Problem hier gelöst habe, werde ich erst einmal abtauchen und mich mit verschiedenen grundlegenden Fragen beschäftigen. Doch leider braucht man für viele "dumme und naive" Fragen die Hilfe, die hier im Forum tatsächlich gegeben wird. Ich danke allen dafür auch wenn es mit mir so manches Mal nervig ist. Es tut mir leid und ich will nicht aufdringlich sein, doch so kurz vor dem Ziel möchte ich nicht aufgeben, auch wenn ich Stunden und Tage an so einem verfluchten Ding sitze und einfach nicht weiter komme. Was mache ich falsch? Ist die Prochedure SaveToDB in der letzten Fassung so richtig?????? Warum wird Insert nie aufgerufen???? |
AW: Kundenliste
[QUOTE=EdAdvokat]
Zitat:
gespeichert werden und eine Id bekommen - das entspricht doch dem insert - oder? Grüße Klaus |
AW: Kundenliste
ich verzweifele: obwohl beim Hinzufügen die ID mit -1 ausgewiesen wird geht das Programm (Debugger) beim Speichern (SavetoDB) in den Bereich UPDATE????
Was ist hier los? Es sollte doch bei -1 nach INSERT gehen. Was mache ich wohl falsch??? Stimmt die Procedure SaveToDB??????? Habe es jetzt neu gefasst:
Delphi-Quellcode:
Ich habe einen Haltepunt auf
procedure TCustomerList.SavetoDB(con: TZConnection);
var zqryMain: TZQuery; i: Integer; begin zqryMain:=TZQuery.Create(nil); try zqryMain.connection:=con; for i := 0 to self.Count-1 do begin if self[i].ID>-1 then begin zqryMain.SQL.Text:='UPDATE WARENVERKAUF1 SET KDNR=:KNR, NAME =:NAM, VORNAME=:VNA, FIRMA=:FIR, PRODUKT=:PRO, ANZAHL=:ANZ, PREIS=:PRE WHERE ID=:CID'; zqryMain.params.parseSQL(zqryMain.sql.text, True); zqryMain.Params.ParamValues['CID']:=self[i].ID; //update zqryMain.params.ParamValues['KNR']:=self[i].KDNR; zqryMain.params.paramValues['NAM']:=self[i].Name; zqryMain.params.paramValues['VNA']:=self[i].Vorname; zqryMain.params.ParamValues['FIR']:=self[i].Firma; zqryMain.Params.ParamValues['PRO']:=self[i].Produkt; zqryMain.Params.ParamValues['ANZ']:=self[i].Anzahl; zqryMain.params.paramValues['PRE']:=self[i].Preis; zqryMain.ExecSQL; end; begin if self[i].ID=-1 then begin zqryMain.SQL.text:='INSERT INTO WARENVERKAUF1(KDNR,NAME,VORNAME,FIRMA,PRODUKT,ANZAHL,PREIS) VALUES(:KNR, :NAM, :VNA, :FIR, :PRO, :ANZ, :PRE)'; zqryMain.params.parseSQL(zqryMain.sql.text, True); zqryMain.params.ParamValues['KNR']:=self[i].KDNR; //insert zqryMain.params.paramValues['NAM']:=self[i].Name; zqryMain.params.paramValues['VNA']:=self[i].Vorname; zqryMain.params.ParamValues['FIR']:=self[i].Firma; zqryMain.Params.ParamValues['PRO']:=self[i].Produkt; zqryMain.Params.ParamValues['ANZ']:=self[i].Anzahl; zqryMain.params.paramValues['PRE']:=self[i].Preis; zqryMain.ExecSQL end; end; end; finally zqryMain.Free; end; end;
Delphi-Quellcode:
gesetzt und dann geprüft, wass beim Aufruf des Programms und Hinzufügen eines Datensatzes passiert. Leider nicht dass was ich erwartet habe.
zqryMain.Params.ParamValues['CID']:=self[i].ID; //update
Es ist doch nicht zu glauben, dass das letzte Problem einfach nicht klappen soll. Ich will eigentlich an dieser Stelle nicht aufgeben, doch ich weiss einfach nicht weiter. |
AW: Kundenliste
Versuch es doch einmal hiermit:
Delphi-Quellcode:
Gruß
procedure TCustomerList.SavetoDB(con: TZConnection);
var zqryMain: TZQuery; i: Integer; begin zqryMain:=TZQuery.Create(nil); try zqryMain.connection:=con; for i := 0 to self.Count-1 do begin if self[i].ID>-1 then begin zqryMain.SQL.Text:='UPDATE WARENVERKAUF1 SET KDNR=:KNR, NAME =:NAM, VORNAME=:VNA, FIRMA=:FIR, PRODUKT=:PRO, ANZAHL=:ANZ, PREIS=:PRE WHERE ID=:CID'; zqryMain.params.parseSQL(zqryMain.sql.text, True); zqryMain.Params.ParamValues['CID']:=self[i].ID; //update end else begin zqryMain.SQL.text:='INSERT INTO WARENVERKAUF1(KDNR,NAME,VORNAME,FIRMA,PRODUKT,ANZAHL,PREIS) VALUES(:KNR, :NAM, :VNA, :FIR, :PRO, :ANZ, :PRE)'; zqryMain.params.parseSQL(zqryMain.sql.text, True); end; zqryMain.params.ParamValues['KNR']:=self[i].KDNR; zqryMain.params.paramValues['NAM']:=self[i].Name; zqryMain.params.paramValues['VNA']:=self[i].Vorname; zqryMain.params.ParamValues['FIR']:=self[i].Firma; zqryMain.Params.ParamValues['PRO']:=self[i].Produkt; zqryMain.Params.ParamValues['ANZ']:=self[i].Anzahl; zqryMain.params.paramValues['PRE']:=self[i].Preis; zqryMain.ExecSQL; end; end; finally zqryMain.Free; end; end; K-H |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:47 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz