![]() |
Re: Schnellste Insert Möglichkeit für eine DB?
Zitat:
Um wieviele Datensätze geht es? Ich würde es so machen, (in der DB) alle Schüler auf ausgeschieden setzen, Schüler aus Liste in DB suchen, Falls vorhanden Ausgeschieden zurück setzen, falls nicht vorhanden eintragen. (ähnlich mach ich es mit den Personen die ich verwalten darf (ca 3.000 Laufzeit ca 1Min) extra Server/oracle) Diese Listen "Wer nicht vorhanden ist den gibt's nicht mehr" sind natürlich sehr fehleranfällig, es wäre besser wenn es zu jedem Namen so etwas wie eine Arbeitsanweisung gäbe (neu anlegen / löschen / auf vorhanden sein prüfen) Gruß K-H |
Re: Schnellste Insert Möglichkeit für eine DB?
Moin,
genauso machen wir das ja auch im Moment. Und natürlich ist das Fehleranfällig, wenn man aber nur die Listen bekommt, wer alles ander Schule ist, dann kann man da nicht sehr viel anderes machen. Einzig, man könnte alle Schüler zum Schluss anzeigen, die gelöscht werden sollen. Aber naja es ist eben nicht nur eine Personenliste, weshalb aktuell Personen ohne Klasse nicht existieren können. Aber meine Frage ist halt, ob man quasi ganz am Anfang ein Query erstellen kann, auch wenn zwischenzeitlich andere Anweisungen durchgeführt werden. Wobei man könnte sogar mehrere Queries benutzen. Einmal ein „Schülerhinzufügen“-Query und ein „Schülervorhanden“-Query. MfG Fabian PS: Es geht ingesamt nur um 1000 Schüler(innen). |
Re: Schnellste Insert Möglichkeit für eine DB?
Zitat:
Zitat:
Streng genommen gibt es keine "Schülerhinzufügen"-Abfrage nur eine Einfügen-Anweisung. Könntest Du vllt etwas Pseudo-Code produzieren? Gruß K-H |
Re: Schnellste Insert Möglichkeit für eine DB?
Moin,
naja es ist schon etwas her (6 Monate) und habe nicht mehr die genauen Spezifikationen vor Augen, aber das Problem ist, dass jede Person einen sogenannten Hauptkurs hat. Dieser Hauptkurs ist entweder ungültig und definiert damit einen Lehrer oder eben ein Schüler, dafür muss es aber ein Hauptkurs geben. Ich habe demnächst wieder eine 6 Stunden Zugfahrt vor mir, wo ich mir evtl. das mal anschaue! Aber die Frage die sich mir stellt, ist halt ob ich zwei Queries parallel laufen haben kann. Und Pseudocode, öhrm, ich hoffe der Code ist soweit selbst erklärend:
Delphi-Quellcode:
Ich weiß, es gibt einige Stellen die man mit Parametern machen sollte, aber darum wollte ich mich eh erst in der nächsten Woche kümmern.
procedure TDatenbankStationBasis.SchuelerEinlesen(const sDateiname: string);
const ANZAHL_ERSTER_SCHRITTE = 11; var oExcel : TExcelApplication; oWorkbook : _WorkBook; oWorksheet : _WorkSheet; cLCID : Cardinal; i, j, iLehrerID, iJahrgang, iSchuelerAnzahl, iPersonenID : Integer; sKlasse, sKlassenlehrer, sName, sVorname : string; ILLehrerIDs, ILKursIDs, ILSchuelerIDs : TIntegerList; SLLehrerNachnamen, SLKursNamen, SLSchuelerNamen, SLSchuelerVornamen, SLSchuelerNachnamen : TStrings; ILPersonenIDs: TIntegerList; ILExemplarIDs: TIntegerList; i_cAnzahlSchritte: Integer; iAktuellerSchritt, iUeberAllesSchritt : Integer; iKursID: Integer; begin oExcel := TExcelApplication.Create(nil); ILLehrerIDs := TIntegerList.Create; ILKursIDs := TIntegerList.Create; ILSchuelerIDs := TIntegerList.Create; ILPersonenIDs := TIntegerList.Create; ILExemplarIDs := TIntegerList.Create; SLLehrerNachnamen := TStringList.Create; SLSchuelerNamen := TStringList.Create; SLSchuelerVornamen := TStringList.Create; SLSchuelerNachnamen := TStringList.Create; SLKursNamen := TStringList.Create; try cLCID := GetUserDefaultLCID; oExcel.Connect; // Exceldatei laden try oWorkbook := oExcel.Workbooks.Open(sDateiname, emptyParam, true, emptyParam, emptyParam, emptyParam, emptyParam, emptyParam, emptyParam, emptyParam, emptyParam, emptyParam, emptyParam, emptyParam, emptyParam, cLCID); except on E : EOleSysError do begin raise EExcelNichtGefunden.CreateDef(e.Message); end; end; i_cAnzahlSchritte := oExcel.Worksheets.Count + 2; iAktuellerSchritt := 0; iUeberAllesSchritt := 0; // Alle LehrerIDs auslesen ... GeneriereDoppelFortschritt(i_cAnzahlSchritte, ANZAHL_ERSTER_SCHRITTE, iUeberAllesSchritt, iAktuellerSchritt, 'Einlesen starten', hstVorbereitung); GibLehrerIDs(ILLehrerIDs); GeneriereDoppelFortschritt(i_cAnzahlSchritte, ANZAHL_ERSTER_SCHRITTE, iUeberAllesSchritt, iAktuellerSchritt, 'LehrerIDs puffern', hstVorbereitung); // ... und dessen Nachnamen speichern GibNamenZuPIDL(SLLehrerNachnamen, ILLehrerIDs); ILLehrerIDs.IntegrateInStrings(SLLehrerNachnamen); GeneriereDoppelFortschritt(i_cAnzahlSchritte, ANZAHL_ERSTER_SCHRITTE, iUeberAllesSchritt, iAktuellerSchritt, 'Lehrer Nachnamen puffern', hstVorbereitung); // Dann die KursIDs einlesen ... GibKursIDs(ILKursIDs); GeneriereDoppelFortschritt(i_cAnzahlSchritte, ANZAHL_ERSTER_SCHRITTE, iUeberAllesSchritt, iAktuellerSchritt, 'Kurse puffern', hstVorbereitung); // ... und dessen Namen speichern GibKursnamenZuKIDL(SLKursNamen, ILKursIDs); GeneriereDoppelFortschritt(i_cAnzahlSchritte, ANZAHL_ERSTER_SCHRITTE, iUeberAllesSchritt, iAktuellerSchritt, 'Kursnamen puffern', hstVorbereitung); // Alle SchülerIDs einlesen ... GibSchuelerIDs(ILSchuelerIDs); GeneriereDoppelFortschritt(i_cAnzahlSchritte, ANZAHL_ERSTER_SCHRITTE, iUeberAllesSchritt, iAktuellerSchritt, 'Schüler puffern', hstVorbereitung); // ... und dessen Nachnamen speichern ... GibNamenZuPIDL(SLSchuelerNachnamen, ILSchuelerIDs); GeneriereDoppelFortschritt(i_cAnzahlSchritte, ANZAHL_ERSTER_SCHRITTE, iUeberAllesSchritt, iAktuellerSchritt, 'Schüler Nachnamen puffern', hstVorbereitung); // ... sowie dessen Vornamen ... GibVornamenZuPIDL(SLSchuelerVornamen, ILSchuelerIDs); GeneriereDoppelFortschritt(i_cAnzahlSchritte, ANZAHL_ERSTER_SCHRITTE, iUeberAllesSchritt, iAktuellerSchritt, 'Schüler Vornamen puffern', hstVorbereitung); // ... und diese dann kombinieren for j := 0 to SLSchuelerNachnamen.Count - 1 do SLSchuelerNamen.Add(SLSchuelerNachnamen[j] + '&' + SLSchuelerVornamen[j]); GeneriereDoppelFortschritt(i_cAnzahlSchritte, ANZAHL_ERSTER_SCHRITTE, iUeberAllesSchritt, iAktuellerSchritt, 'Schüler Namen puffern', hstVorbereitung); // Alle Kurse und Kurszuweisungen löschen, die nicht Hauptkurse sind EntferneZuSQL('Kurse', 'Hauptkurs <> 0'); GeneriereDoppelFortschritt(i_cAnzahlSchritte, ANZAHL_ERSTER_SCHRITTE, iUeberAllesSchritt, iAktuellerSchritt, 'Kurse löschen', hstVorbereitung); EntferneZuSQL('Kurszuweisungen', 'KursID IN (SELECT KursID FROM Kurse WHERE Hauptkurs <> 0)'); GeneriereDoppelFortschritt(i_cAnzahlSchritte, ANZAHL_ERSTER_SCHRITTE, iUeberAllesSchritt, iAktuellerSchritt, 'Kurszuweisungen löschen', hstVorbereitung); // Und dann die Schüler auf obsolet setzen ExecSQL('UPDATE Personen SET Obsolet = -1 WHERE HauptKursID != 0'); GeneriereDoppelFortschritt(i_cAnzahlSchritte, ANZAHL_ERSTER_SCHRITTE, iUeberAllesSchritt, iAktuellerSchritt, 'Personen zum Löschen markieren', hstVorbereitung); BeginTransaction; for i := 1 to oExcel.Worksheets.Count do begin oWorksheet := oWorkbook.Sheets[i] as _WorkSheet; sKlasse := oWorksheet.Name; j := 1; while (CharInSet(sKlasse[j], ['0'..'9'])) and (j <= Length(sKlasse)) do Inc(j); iJahrgang := StrToIntDef(Copy(sKlasse, 1, j - 1), -1); // Lehrer einlesen (wenn der Name zu lang ist, ist der in der nächsten // Zeile) sKlassenlehrer := oWorksheet.Cells.Item[2, 5].Value + oWorksheet.Cells.Item[3, 5].Value; // Anrede entfernen Delete(sKlassenlehrer, 1, 5); GeneriereDoppelFortschritt(i_cAnzahlSchritte, 1, iUeberAllesSchritt, iAktuellerSchritt, 'Tabelle/Klasse "' + sKlasse + '" wird eingelesen', hstEinlesen); if (iJahrgang >= 0) and (Length(sKlassenlehrer) > 0) then begin // Position des Nachnamens feststellen iLehrerID := SLLehrerNachnamen.IndexOf(sKlassenlehrer); // Lehrer existiert nicht if iLehrerID = -1 then begin // Dann nachfragen was geschehen soll if Assigned(fELF) then fELF(self, sKlassenlehrer, sKlasse, iLehrerID); if (iLehrerID = -1) or (not boIstLehrerZuPID(iLehrerID)) then raise ELehrerNichtGefunden.Create('Kein Lehrer zur Kurszuweisung gefunden.'); end else //Lehrer vorhanden begin iLehrerID := Integer(SLLehrerNachnamen.Objects[iLehrerID]); ExecSQL('UPDATE Personen SET Obsolet = 0 WHERE PersonenID = '+ IntToStr(iLehrerID)); end; // Gültige ID if iLehrerID > 0 then begin // Neuen Kurs anlegen iKursID := iFuegeKursHinzu(iJahrgang, iLehrerID, sKlasse, True); j := 10; while oWorksheet.Cells.Item[j, 2].Value <> '' do begin Inc(j); end; iSchuelerAnzahl := j - 10; if iSchuelerAnzahl = 0 then GeneriereDoppelFortschritt(i_cAnzahlSchritte, 1, iUeberAllesSchritt, iAktuellerSchritt, 'Klasse ' + sKlasse + ' eingelesen', hstEinlesen); for j := 0 to iSchuelerAnzahl - 1 do begin sName := oWorksheet.Cells.Item[j + 10, 2].Value; sVorname := sName; Delete(sName, Pos(',', sName), Length(sName) - Pos(',', sName)+1); Delete(sVorname, 1, Pos(',', sVorname) + 1); sName := Trim(sName); sVorname := Trim(sVorname); iPersonenID := ILSchuelerIDs.IntegersDef(SLSchuelerNamen.IndexOf(sName + '&' + sVorname), -1); // Überprüfen ob die PersonenID gültig ist if iPersonenID = -1 then begin iFuegeSchuelerHinzu(sName, sVorname, iKursID); end else begin WeiseHauptKursIDZuPIDZu(iPersonenID, iKursID); end; // Fortschritssbalken updaten GeneriereDoppelFortschritt(i_cAnzahlSchritte, iSchuelerAnzahl, iUeberAllesSchritt, iAktuellerSchritt, 'Klasse: ' + sKlasse + ' Schüler: ' + sName + ' eingelesen', hstEinlesen); end; end else begin GeneriereDoppelFortschritt(i_cAnzahlSchritte, 1, iUeberAllesSchritt, iAktuellerSchritt, 'Klasse: ' + sKlasse + ' übersprungen', hstEinlesen); end; end else begin GeneriereDoppelFortschritt(i_cAnzahlSchritte, 1, iUeberAllesSchritt, iAktuellerSchritt, 'Unbekannte Klasse übersprungen', hstEinlesen); end; end; // Zu entfernen Einträge wieder entfernen. ILSchuelerIDs.Clear; ILLehrerIDs.Clear; SLLehrerNachnamen.Clear; SLKursNamen.Clear; SLSchuelerNamen.Clear; SLSchuelerVornamen.Clear; SLSchuelerNachnamen.Clear; // Gib alle IDs die nun obsolet sind GeneriereDoppelFortschritt(i_cAnzahlSchritte, 1, iUeberAllesSchritt, iAktuellerSchritt, 'Überprüfe Datenbank', hstAufraeumen); GibObsoleteIDs(ILPersonenIDs, true); if ILPersonenIDs.Count = 0 then GeneriereDoppelFortschritt(i_cAnzahlSchritte, 1, iUeberAllesSchritt, iAktuellerSchritt, 'Löschen abgeschlossen', hstAufraeumen); for i := 0 to ILPersonenIDs.Count - 1 do begin if iGibAnzahlAusgeliehenZuPID(ILPersonenIDs[i]) = 0 then begin // Benutzer hat keine Bücher ausgeliehen und ist obsolet -> kann // gelöscht werden, aber zuerst die Kurszuweisungen ExecSQL('DELETE FROM Kurszuweisungen WHERE PersonenID = ' + IntToStr(ILPersonenIDs[i])); ExecSQL('DELETE FROM Personen WHERE PersonenID = ' + IntToStr(ILPersonenIDs[i])); end; GeneriereDoppelFortschritt(i_cAnzahlSchritte, ILPersonenIDs.Count, iUeberAllesSchritt, iAktuellerSchritt, 'Lösche Personen', hstAufraeumen); end; Commit; oWorkbook.Close(False, emptyParam, emptyParam, cLCID); oExcel.Quit; finally oExcel.Free; ILLehrerIDs.Free; ILSchuelerIDs.Free; ILPersonenIDs.Free; ILKursIDs.Free; ILExemplarIDs.Free; SLKursNamen.Free; SLLehrerNachnamen.Free; SLSchuelerNamen.Free; SLSchuelerNachnamen.Free; SLSchuelerVornamen.Free; end; end; MfG Fabian |
Re: Schnellste Insert Möglichkeit für eine DB?
Hallo xzise,
zunächst einmal, diese Auftrennung in Nachname und Vorname, und die nachfolgende Kombination zumindestens bei den Schülern erscheint mir doch sehr obskur. Oder habe ich die Funktion als Index übersehen? wenn ich Deine Sourcen auf die SQL-Relevanz verkürze bleibt nur noch das übrig:
Code:
Zunächst steht da ziemlich verloren ein einsames BeginTransaction, nur noch gefolgt von Datenmanipulationen aber ohne irgendeinen Datenbankbezug (was hab ich da übersehen?).// Alle Kurse und Kurszuweisungen löschen, die nicht Hauptkurse sind EntferneZuSQL('Kurse', 'Hauptkurs <> 0'); EntferneZuSQL('Kurszuweisungen', 'KursID IN (SELECT KursID FROM Kurse WHERE Hauptkurs <> 0)'); // Und dann die Schüler auf obsolet setzen ExecSQL('UPDATE Personen SET Obsolet = -1 WHERE HauptKursID != 0'); BeginTransaction; Und was macht EntferneZuSql ? Wenn ich das richtig verstanden habe, wird jedes mal auf der Datenbank eine wílde Datensammlung erzeugt, die dann wieder bereinigt wird? (Hauptkurs oder nicht) Dieser "Datenmissbrauch" ist die Grundlage von Chaos-Programmen. Wenn man das Geschlecht einer Person bestimmen/dokumentieren will, dann braucht's ein Feld Geschlecht und keine Umwege über Vorname, Schuhgröße, Haarlänge und was für kreative Ideen da noch herum schwirren. (Übrigens, das der Titel bei Lehrer die ersten fünf Stellen des Namens belegt ist auch so eine Krücke die Fehler geradezu provoziert) Da sollte das Konzept, das dahinter steht einmal überdacht werden. Wenn es Dir darum geht, Dein Programm zu beschleunigen, dann solltest Du von diesem
Code:
Abstand nehmen, denn das ist ein echter Zeitfresser.
GeneriereDoppelFortschritt(i_cAnzahlSchritte...
Zitat:
gruß K-H |
Re: Schnellste Insert Möglichkeit für eine DB?
Moin,
ich weiß, es ist nicht gerade ideal gelöscht, aber wir bekommen halt eine Liste mit Namen von Schülern. Das heißt wir können nicht (was wesentlich besser ist) testen, ob eine PersonenID noch existiert, sondern stattdessen uns darauf verlassen, dass es keine zwei Personen mit den gleichen Vornamen und Nachnamen parallel gibt. Und ich verstehe deine „SQL-Relevanz“ nicht. Das alles vor den BeginTransaction bereitet die Datenbank vor. Es ist halt nicht eben nur eine einfache Personenliste, sondern wir speichern zusätzlich auch die Kurse/Klassen der einzelnen Schüler. Dazu gibt es die Tabelle Kurse und Kurszuweisungen. Der erste Befehl, löscht nun alle Kurse, die ein Hauptkurs sind während der zweite Befehl dann alle Zuweisungen löschen soll. Das heißt er löscht die Information, welche Schüler in den Kursen waren, die gelöscht wurden. Okay ich glaube das muss andersherum ausgeführt werden. Der dritte Befehl, setzt dann präventiv alle Schüler erst mal auf überflüssig (der Hauptkurs ist eben nicht 0, das sind die Lehrer) also Obsolet ist nicht 0. So und dann kommen wir in den interessanten Bereich: Nun gehen wir jede Klasse der Exceltabelle durch. Oben rechts steht dann immer "Herr/Frau ...", nagut das es immer 5 sind, ist nicht direkt gegeben, das schaue ich mir mal an, wie man das anders macht (wohl eher dann mit Pos etc.). Also ermittelt er erstmal den Lehrer/die Lehrerin, wenn sie nicht existiert, MUSS sie erstellt werden, weil daraufhin dann wieder ein neuer Kurs angelegt wird und dieser MUSS umbedingt eine Valide LehrerID haben! Dann geht er jeden Schüler dieses Kurses durch und liest den Namen ein und prüft, ob er bereits existiert. Wenn er nicht existiert (bzw. der Vorname und Nachname), dann ruft er iFügeSchülerHinzu auf, welche einen Schüler hinzufügt. Alternativ setzt er den Schüler auf nicht überflüssig (Obsolet = 0). Zusätzlich setzt er dann auch die neue HauptkursID. Okay ich habe mich zu lange nicht damit beschäftigt, weshalb da nicht nur zwei SQL Statements vorkommen: Um eine Person hinzufügen (das ist der Inhalt von iFuegeSchuelerHinzu):
SQL-Code:
So und für jede Person gilt das (das ist der Inhalt von WeiseHauptKursIDZuPIDZu, welche von iFuegeSchuelerHinzu aufgerufen wird!):
INSERT INTO Personen(Name,Vorname,HauptKursID,Obsolet) VALUES ("AName", "AVorname" , 0, 0)
SQL-Code:
Okay, das DELETE Statement ist überflüssig, weil wir zu Anfang da ja aufgeräumt haben.
DELETE FROM Kurszuweisungen WHERE PersonenID = ASchuelerID AND KursID = AKursID
UPDATE Personen SET HauptKursID = AKursID WHERE PersonenID = APersonenID INSERT INTO Kurszuweisungen (PersonenID, KursID) VALUES (ASchuelerID, AKursID) Und Alle Werte mit A.... sind Variablen und beim hinzufügen der Person, bekomme ich die SchülerID zurückgegeben. Und ganz zum Schluss, lösche ich dann alle Schüler, die obsolet markiert wurden. Sofern das nicht weiter zu optimieren ist, lasse ich dennoch das GeneriereDoppelFortschritt( drin, damit der Anwender eine Rückmeldung bekommt. MfG Fabian |
Re: Schnellste Insert Möglichkeit für eine DB?
Hai ihr,
hat das mti der "Schülerliste" noch etwas mit dem Ursprungsthema zu tun? |
Re: Schnellste Insert Möglichkeit für eine DB?
@Sharky
Ich denke jetzt wieder @xZise jetzt hast Du die Chance über die Parameter Geschwindigkeit zu machen z.b. so:
Delphi-Quellcode:
So sollte es im Prinzip ablaufen. Der Einfacheit halber ist bei mir Liste keine Stringliste, aber ich denke das Prinzip ist klar.
q.sqltext:='INSERT INTO Personen(Name,Vorname,HauptKursID,Obsolet) VALUES (:AName, :AVorname , 0, 0);
for i:=0 to liste.count-1 do begin q.parambyname('Aname').asstring:=liste[i].Name; q.parambyname('Avorname').asstring:=liste[i].Vorname; q.sqlexec; end;
SQL-Code:
auch hier solltest Du (wenn möglich!) erst alle DELETE-Anweisungen, dann die UPDATEs und dann die INSERTs ausführen lassen.
DELETE FROM Kurszuweisungen WHERE PersonenID = ASchuelerID AND KursID = AKursID
UPDATE Personen SET HauptKursID = AKursID WHERE PersonenID = APersonenID INSERT INTO Kurszuweisungen (PersonenID, KursID) VALUES (ASchuelerID, AKursID) Natürlich unter Ausnutzung der Parameter. was die Transaktion angeht, würde ich jede for i:=.Schleife in eine Transaktion packen, aber das ist eine reine Bauchempfehlung. Gruß K-H |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:22 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz