| Autor |
Beitrag |
drknigge
Hält's aus hier
Beiträge: 7
|
Verfasst: Mi 01.12.04 23:00
Hallo zusammen,
ich habe ein kleines Parkplatzverwaltungsprogramm mit Borland Delphi 6 programmiert. Die Daten habe ich alle in einer Access Datenbank gespeichert.
Auf der Verwaltungsoberfläche werden alle Mitarbeiter mit zugehöriger Schicht und dessen Kennzeichen angezeigt. Die Daten werden in einer TListView angezeigt. Für jede Zeile, muss er sich Daten aus ca. 3 verschiedenen Tabellen holen.
Jetzt ist das Problem, dass mitlerweile knapp 600 Mitarbeiter eingetragen sind und jetzt der Aufruf der Daten ewig lange dauert. Im Betrieb soll die Software dann auf einem Netzlaufwerk laufen, was die Zugriffszeit ja nocheinmal erheblich verlängert.
Liegt das Geschwindigkeitsproblem an Access oder an meiner Programmierweise? Gibt es Alternativen oder Tipps Performance sparend zu programmieren, bzw. auf Datenbanken zu zugreifen?
Mir wurde eine Möglichkeit genannt, mit einer einzigen Datenbankabfrage all derer Daten in eine TList zu speichern und dann, wenn ich dass richtig verstanden habe die TListView mit den Daten aus TList zu füllen. Das würde wohl wesentlich schneller gehen. Allerdings bin ich erst relativ kurz bei Delphi und weiß absolut nicht wie ich das realisieren soll. Wäre um Erklärungen, Codebeispiele usw. sehr sehr dankbar.
Viele Grüße
Maik
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mi 01.12.04 23:29
Bester Tipp:
Anzahl der Zugriffe auf DB verringern
Zusammenfassen deiner Abfrage in einer Query
Bei Filtern immer die Regel zuerst angeben, die am wenigsten Datensätze übrig lässt.
Kombinieren deiner Queries durch INNER JOINs.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
Klabautermann
      

Beiträge: 6366
Erhaltene Danke: 60
Windows 7, Ubuntu
Delphi 7 Prof.
|
Verfasst: Do 02.12.04 09:23
Hallo,
dann bliebe natürlich noch die Frage, ob du beim ListView die BeginUpdate und EndUpdate Methoden verwendest? Wenn nicht, bremmst du dein Programm damit erheblich aus.
Das ganze müsste dann so aussehen, das try finally ist dabei für den Fall eines Fehlers extrem wichtig, den ohne dieses könnte dein ListView unbedinbar werden.
Delphi-Quelltext 815: 816: 817: 818: 819: 820: 821:
| { ... } ListView1.Items.BeginUpdate; try ListView1.Items.Clear; finally ListView1.Items.EndUpdate; end; |
Dies gild natürlich ergänzend zu dem was BenBE vorgeschlagen hat.
Gruß
Klabautermann
|
|
drknigge 
Hält's aus hier
Beiträge: 7
|
Verfasst: Do 02.12.04 12:39
Ich erkläre vielleicht mal wie genau meine Tabellen aufgebaut sind. Dann könnt ihr vielleicht konkretere Vorschläge geben, wie ich die Abfragen reduziere.
Tabellen:
tbl_personal (Personaldaten wie Name, Nummer usw)
tbl_kennzeichen (Tabelle mit personalnr und kennzeichen, pro Mitarbeiter können 3 Einträge vorhanden sein)
tbl_bereiche (Parkbereiche, so wie Parkplatz oben, Parkplatz hauptstraße usw)
tbl_parkplaetze (Parkplatznummern, mit bereich_id und status ob belegt)
tbl_beschaeftigung (Beschaeftigungsarten wie Teilzeit, Vollzeit, Schicht)
tbl_status (Statusarten wie Parkplatz zugeteilt, auf Warteliste usw.)
tbl_abteilung (Abteilungen im Unternehmen)
Das Programm läuft jetzt wie folgt ab:
Personalquery (SELECT *)
Dann wird für jeden Datensatz folgendes gemacht:
Query auf tbl_beschaeftigung - Beschäftigungsart des Mitarbeiters auslesen
Query auf tbl_status - Status d. M. auslesen
Query auf tbl_abteilungen - Abteilung d. M. auslesen
wenn Parkplatz zugeilt
Query auf tbl_parkplaetze - Parkplatznr d. M. auslesen
Query auf tbl_bereiche - Parkbereich d. M. auslesen
dann
Query auf tbl_kennzeichen - Kennzeichen d. M. auslesen
Dann sind alle benötigten Daten ausgelesen und die Zeile wird in TListView hinzugefügt. Das ganze dann halt für jeden Datensatz. Bei ca. 600 wären dass dann 3600 Datenbankquerys. Ganz schön mies.
Freue mich auf eure Tipps.
Edit: habe mich jetzt mal an einem INNER JOIN probiert, aber er meldet Syntaxfehler. Wo liegt das Problem?
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| fPersonalReadQuery.Connection := Sys.ADOConnection; fPersonalReadQuery.SQL.Text := 'SELECT Personal.*, ' +'Park.*, ' +'Bereich.*, ' +'Abteilung.*, ' +'Beschaeftigung.*, ' +'Status.* '
+'FROM tbl_personal AS Personal INNER JOIN tbl_parkplaetze AS Park ON Personal.parkplatz_id = Parkplatz.id_parkplatz ' +'INNER JOIN tbl_bereiche AS Bereich ON Park.bereich_id = Bereich.id_bereich ' +'INNER JOIN tbl_abteilungen AS Abteilung ON Personal.abteilung_id = Abteilung.id_abteilung ' +'INNER JOIN tbl_status AS Status ON Personal.status_id = Status.id_status ' +'INNER JOIN tbl_beschaeftigung AS Beschaeftigung ON Personal.beschaeftigung_id = Beschaeftigung.id_beschaeftigung'; fPersonalReadQuery.Open; fPersonalReadQuery.First; |
Viele Grüße
Maik
Moderiert von Klabautermann: Code durch Delphi-Tags ersetzt.
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Do 02.12.04 14:48
Kann ich dir auf Anhieb bei der Anweisung erstmal nicht sofort sagen, geb dir aber einen Tipp:
Aber probier mal, ob es funzt, wenn du den SQL-Befehl mit einem Semikolon abschließt. Die SQL-Parser sind damit manchmal SEHR genau, vor allem der von Access
Access hat einen Abfrage-Editor. In diesem brauchst du einfach nur deine Abfrage zu erzeugen und dann auf die SQL-Ansicht zu wechseln. Die dort aufgeführte Anweisung verstehen dann die ADO-Komponenten bzw. die BDE, d.h. dass du diese einfach nur kopieren brauchst.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
jasocul
      
Beiträge: 6395
Erhaltene Danke: 149
Windows 7 + Windows 10
Sydney Prof + CE
|
Verfasst: Do 02.12.04 14:59
Der Syntax-Felhler kommt, da du zwar im select-Bereich mehrere Tabellen anführst, diese aber im From-Bereich gar nicht einliest.
Zusätzlich fehlt dann noch die Where-Klausel mit den verschiedenen and.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| fPersonalReadQuery.SQL.Text := 'SELECT Personal.*, ' +'Park.*, ' +'Bereich.*, ' +'Abteilung.*, ' +'Beschaeftigung.*, ' +'Status.* '
+'FROM tbl_personal AS Personal, tbl_park as Park, tbl_bereich as Bereich .... ' +'where INNER JOIN tbl_parkplaetze AS Park ON Personal.parkplatz_id = Parkplatz.id_parkplatz ' +' and INNER JOIN tbl_bereiche AS Bereich ON Park.bereich_id = Bereich.id_bereich ' +'and INNER JOIN tbl_abteilungen AS Abteilung ON Personal.abteilung_id = Abteilung.id_abteilung ' +'and INNER JOIN tbl_status AS Status ON Personal.status_id = Status.id_status ' +'and INNER JOIN tbl_beschaeftigung AS Beschaeftigung ON Personal.beschaeftigung_id = Beschaeftigung.id_beschaeftigung'; fPersonalReadQuery.Open; fPersonalReadQuery.First; |
Sieh dir meine Korrekturen an.
|
|
greg
      
Beiträge: 119
Windows2000, WindowsXP
D5 Prof
|
Verfasst: Do 02.12.04 15:08
Hallo DrKnigge,
ich hab mich auch schon mit Access Datenbanken rumgeschlagen. Grundsätzlich ist Access deutlich langsamer, als Paradox, besonders, je größer die Datenmengen sind. Übers Netzwerk ist der Zugriff wirklich schon fast ne Zumutung. Aber das nur am Rande.
Das eigentliche Dilemma ist, dass Access mit SQL etwas pingelig ist. Daher habe ich dann zunächst die Queries mit dem Assistenten in Access zusammengebastelt, mir dann das SQl anzeigen lassen und bei Bedarf noch modifiziert. Wenn es in Access funktioniert, dann tut es auch von Delphi aus ... also einfach nach Delphi kopieren und einbauen. Oder aber Du definierst Dir die Abfrage dauerhaft in Access. Soll heissen, Du greifst von Delphi aus mit einer Table-Komponente lesend auf die Abfrage in Access zu, die die Daten enthält, die Du anzeigen möchtest.
Dies führt gleichzeitig zu einem weiteren Lösungsansatz. Ob Dir das weiterhilft, musst Du selbst einschätzen können. Wenn die Ergebnisliste nicht ständig neu aufgebaut werden muss, sondern nur unter ganz bestimmten Bedingungen, kannst Du die Ergebnisse auch in eine Tabelle wegschreiben, aus der man dann entsprechend fix die Daten auslesen kann.
Noch eine Empfhelung ... ich würde mich bei den Queries auf die Informationen beschränken, die wirklich notwendig sind ... also z.B. wer soll wo parken. Die Personalnummer oder Beschaeftigungsart ist dabei ja wohl eher weniger von Bedeutung, auch wenn Du Sie für die Auswertung benötigst.
Gruß
Gregor
|
|
drknigge 
Hält's aus hier
Beiträge: 7
|
Verfasst: Do 02.12.04 16:34
Super Hilfe hier!
Das mit dem INNER JOIN Funktioniert jetzt, nachdem ich mir die SQL Anweisung in Access zusammengestellt habe.
Habe auch alle Tabellen mit in dieses Join bekommen, bis auf die Tabelle Kennzeichen. Wenn ich ohne die Kennzeichen die TListView füllen lasse, dauerts jetzt nur noch 1 sek statt ca. 20 - 30.
Das Problem ist jetzt dass ich die Tabelle so aufgebaut habe:
ID ---- Personalnr ----- Kennzeichen
1 --------- 1 ------------ TEST1
2 --------- 1 ------------ TEST2
3 --------- 2 ------------ TEST3
So kann ich sie nicht einfach so mit in die Abfrage reinpacken, weil er sich dann immer nur das zuerst eingetragene Kennzeichen einer Personalnr nimmt und die anderen ignoriert!
Was könnt ihr mir empfehlen?
|
|
greg
      
Beiträge: 119
Windows2000, WindowsXP
D5 Prof
|
Verfasst: Do 02.12.04 16:42
Hallo DrKnigge,
so speziell, wie Deine Frage ist, reicht es nicht aus, wenn Du uns nur die Struktur einer Tabelle mitteilst. Wir brauchen die Beziehungen zu anderen Tabellen (über welche Spalten stellst Du die Verbindungen her?), um Dir hier wirklich weiterhlefen zu können. Was mir nur auffält ... warum die Tabelle Kennzeichen? Du wirst doch wohl polizeiliche Kennzeichen verwenden? Die sind doch eh eindeutig? Warum trägst Du es nicht beim Personal ein?
Gruß
Gregor
|
|
drknigge 
Hält's aus hier
Beiträge: 7
|
Verfasst: Do 02.12.04 16:49
In der Tabelle Kennzeichen und Personal ist die Spalte personalnr identisch. Daran wird zugeordnet, welches Kennezeichen wem gehört.
Ja ich verwende die polizeilichen Kennzeichen. Ich hatte mir das Anfangs so gedacht, dass ich das Programm erweiterbar halten wollte, so dass irgendwann mehr als 3 Kennzeichen angelegt werden können. Momentan ist diese Zahl auf 3 beschränkt. Finde diese Lösung mitlerweile auch blöd und umständlich, aber habe jetzt schon zuviele Daten um das mal eben umzustricken...
|
|
greg
      
Beiträge: 119
Windows2000, WindowsXP
D5 Prof
|
Verfasst: Do 02.12.04 17:05
Ja, wenn das so ist, hast Du ein Problem. Du bekommst für die Kennzeichen über die Personalnummer keinen eindeutigen Datensatz zurück.
Ich versteh schon Deine Absicht, alle Eventualitäten zu berücksichtigen ... aber manchmal kann man es nun mal nicht perfekt machen. Was möglich wäre, wäre, dass die Mitabeiter ihre Kennzeichen selbst verwalten und dann z.B. auch mal kurzfristig das Kennzeichen eines Mietwagens eintragen können. Jeder Mitarbeiter müsste sich dann für einen bestimmten Tag mit einem Kennzeichen für seine Schicht eintragen. Willst Du das so? Machen die Kolleginnen und Kollegen da mit?
Wenn Du nur eine begrenzte Anzahl von Kennzeichen zuläßt hast Du andersherum das Problem, dass z.B. das Kennzeichen eines Mietwagens nicht registriert ist. Ja und dann?
Also ... warum nicht ein Kennzeichen pro Mitarbeiter registrieren. Und wenn da ein unbekanntes Auto steht, kann man doch wohl ermitteln, wem der Platz zugesprochen ist? Man könnte auch Parkkarten mit Nummern drauf verwenden. Diese Nummern tauchen dann wieder in Deiner Datenbank auf und lassen eine eindeutige Zuordnung zu. Was aber, wenn einer seine Karte vergisst? ... Au weia
Was ist überhaupt mit Besuchern, deren Kennzeichen sicher nicht in Deiner Datenbank sind? Ja Wahnsinn ... Möglichkeiten über Möglichkeiten  is schon stark, was so eine scheinbar einfache Aufgabenstellung für Tücken verbirgt.
Gruß
Gregor
|
|
drknigge 
Hält's aus hier
Beiträge: 7
|
Verfasst: Do 02.12.04 17:27
Es bekommt jeder Mitarbeiter der einen Parkplatz zugewiesen bekommen hat drei Parkausweise, die ebenfalls von diesem Programm erstellt werden. Halt einen Parkausweis pro Kennzeichen. Das ist so geregelt, damit den Mitarbeitern die Möglichkeit geboten wird nicht nur mit ein und dem selben Fahrzeug zu kommen. Besucherparkplätze sind besonders gekennzeichnet und nicht von Mitarbeitern mit Parkausweis belegbar. Dementsprechend werden auch keine Parkausweise für diese Plätze vergeben.
Sonst irgendwelche Tipps wie ich das doch hinkriegen könnte?
|
|
greg
      
Beiträge: 119
Windows2000, WindowsXP
D5 Prof
|
Verfasst: Do 02.12.04 18:09
Ok ... ist ne Möglichkeit ...
Also ... wenn das so ist? Warum die Zahl der Kennzeichen begrenzen? Ich würde es so machen, dass man pro Personalnummer Kennzeichen zuordnen kann und dann in eine der anderen Tabellen zurückschreiben. Beim Personal als Haupt-Kennzeichen oder in die Belegliste, wobei das für den Anwender viel Arbeit bedeutet.
Gruß
Gregor
|
|
drknigge 
Hält's aus hier
Beiträge: 7
|
Verfasst: Do 02.12.04 18:19
Also ich hab mir ein paar Gedanken gemacht.
Habe mir überlegt, dass ich die Kennzeichentabelle in ein Record speicher.
So in der Art KennzeichenRecord mit den Werten:
Persnr
KFZ1
KFZ2
KFZ3
Jetzt ist nur das Problem, kann ich irgendwie den Arraynamen also array[name] als string definieren. Bei mir meckert er und will eine Zahl haben. Quasi als Indexwert. Aber gibt es da keine Möglichkeit dem keine Zahl sondern einen namen als Index zu geben? Oder eine Andere Möglichkeit: Kann ich das Record nach bestimmten werten durchsuchen? Also das ich jetzt sage, suche in Record Persnr = 2932 und gib mir den Index zurück?
Irgendwie so muss das doch zu lösen sein. Wenn der für jeden Datensatz noch das Query für das Kennzeichen machen muss, dann ist das einfach zu langsam! Keine Steigerung zu vorher.
Gruß
Maik
|
|
greg
      
Beiträge: 119
Windows2000, WindowsXP
D5 Prof
|
Verfasst: Do 02.12.04 18:36
Hallo DrKnigge,
Du wirst mich heute nicht mehr los
Du könntest ein Konstrukt wie dieses verwenden, um dort Personalnummer und Kennezeichen abzulegen.
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| type myRecord = Record Kenzzeichen : String; Personalnummer : Integer; end; myRecords = Array of myRecord; |
In OnShow des Hauptformulars Deiner Anwendung kannst Du dann die länge Deines dynamischen Arrays setzen:
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| procedure TForm1.FormShow(Sender: TObject); var x : Integer; begin x := TKennzeichen.RecordCount; SetLength(myRecords, x) end; |
Ansprechen kannst Du die einzelnen Datensätze dann über den Index des Arrays z.B. so:
Delphi-Quelltext 1:
| myRecords[x].Kenneziechen := ... |
wenn Du am Anfang Deine Kennzeichentabelle nach ID sortiert einliest und die Inhalte ins Array schreibst, ist der jeweilige Index Deines Arrays um 1 kleiner als die ID aus Deiner Kennzeichentabelle (Array fängt bei Null an).
Gruß
Gregor
|
|
drknigge 
Hält's aus hier
Beiträge: 7
|
Verfasst: Do 02.12.04 19:14
Du mich aber auch nicht mehr
Die Ansteuerung über die ID bringt mir nichts, da ich dabei keinen Bezug auf die Personalnummer habe. Ich entnehme deinem Posting einfach mal, dass es nicht möglich ist ein Record zu durchsuchen und auch nicht Strings als Array Index zu nehmen.
Was hält du von meiner neuen Idee die Tabelle Kennzeichen in eine TListView einzulesen, aber nicht anzuzeigen. Die Liste kann ich durchsuchen und dann den Index zurückkriegen, so kann ich dann jedes Element der Liste ansprechen. Wäre eine Möglichkeit, oder?
|
|
greg
      
Beiträge: 119
Windows2000, WindowsXP
D5 Prof
|
Verfasst: Do 02.12.04 20:22
... also ... mach es Dir doch nicht so schwer. Was meinst Du, was eine ListView ist? Die ListView baut Datentechnisch doch auch auf ein solches Array auf. Zumindest ist unterscheidet sich das, was Du vor hast nicht von dem, was Du auch mit dem Array machen kannst:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| ... var Personalnr : Integer; Kennz : String; begin Kennz := ''; for i := 0 to myRecords.RecordCount do begin if myRecords[i].Personalnummer = Personalnr then ID Kennz := myRecords[i].Kennzeichen; ShowMessage(Kennz); end; end; |
Das Einzige Problem, was Du dann noch lösen musst, abgesehen davon, dass Du bei dem Algorithmus immer alle Datensätze durchsuchst, ist wenn zu einer Personalnummer mehrere Kennzeichen existieren. Ein Ansatz könnte sein alle Kennzeichen zurückzugeben:
Delphi-Quelltext 1:
| if myRecords[i].Personalnummer = Personalnr then ID Kennz := Kennz + myRecords[i].Kennzeichen; |
Gruß
Gregor
|
|
|