Probleme beim Rechnen in einer Tabelle



  • Hallo!

    Ich habe hier eine kleine Tabelle in der Darlehen verwaltet werden. Will heißen es gibt die Spalte Betrag und Saldo. Wenn ich nun einen neuen Datensatz hinzufüge kommt in der Spalte "Betrag" entweder eine Tilgung oder ein Darlehen dazu und in der Spalte "Saldo" sollte der neue Saldo errechnet werden. Leider klappt das nicht immer richtig. Ich habe deshalb versucht mit einer kleinen Routine diesen Fehler nachträglich zu korrigieren. Leider klappt auch das nicht immer richtig. Hier mal mein Code:

    for (int i = 1; i < ADOQuery3->RecordCount; i++) {
    				cSaldoAlt = ADOQuery3->FieldByName("SALDO")->AsCurrency; // Alten Saldo merken
    				ADOQuery3->Next();
    				// Einen Datensatz vorspringen
    				iDatensatznummer = ADOQuery3->FieldByName("NUMMER")->AsInteger; // Nummer von Datensatz uebergeben
    				cBetragAlt = ADOQuery3->FieldByName("BETRAG")->AsCurrency; // Alten Betrag merken
    				cSaldoNeu = cSaldoAlt + cBetragAlt;		// Neuen Saldo berechnen
    
    				// Neuen Saldo in Tabelle schreiben
    				ADOQuery1->SQL->Clear();
    				ADOQuery1->SQL->Text =
    					"UPDATE automatenverwaltung.darlehen SET SALDO = " +
    					cSaldoNeu + " WHERE NR_KD = '" + IntToStr(iKundennummer)
    					+ "' AND NUMMER = '" + IntToStr(iDatensatznummer) + "'";
    				ADOQuery1->ExecSQL();
    
    			}
    

    Falls ich hier auf dem Holzweg bin und es eine vielleicht viel einfacher und besser funktionierende Lösung geben sollte wäre ich dankbar für Vorschläge.

    LG Maik



  • Welche BCB-Version?
    Welche Datenbank?
    Wie äußern sich die Rechenfehler?
    Wie sieht die ursprüngliche Routine zum Einfügen aus?

    Grundsätzlich sollte man das Rechnen der Datenbank überlassen, die Berechnung also innerhalb des Statements durchführen.



  • Hallo!

    Meine Version ist C++Builder 2010. Die Datenbank die dahintersteckt ist MySQL. Die Rechenfehler äußern sich dahingehend das der Saldo falsch berechnet wird. Die Ursprüngliche Routine zum einfügen folgt hier:

    // Pruefen ob schon ein Darlehen vorliegt oder ob eine Eroeffnung stattfindet
    	if (f_start->ADOQuery1_Darlehen->RecordCount == 0) {
    		int dar1 = MessageDlg("Möchten Sie für den Aufstellplatz '" +
    			f_start->ADOQuery1_Kundenverwaltung->FieldByName("AUFSTELLPLATZ")
    			->AsString + "' ein Darlehen eröffnen?", mtConfirmation,
    			TMsgDlgButtons() << mbYes << mbNo, 0);
    		if (dar1 == mrYes) {
    			/* LNR_Darlehen hochzaelhen und in Tabelle Firmenparameter speichern */
    			f_start->ADOTable1_Firmenparameter->Edit();
    			f_start->ADOTable1_Firmenparameter->FieldByName("LNR_DARLEHEN")
    				->AsInteger = f_start->ADOTable1_Firmenparameter->FieldByName
    				("LNR_DARLEHEN")->AsInteger + 1;
    			f_start->ADOTable1_Firmenparameter->Post();
    			int lnr_darlehen = f_start->ADOTable1_Firmenparameter->FieldByName
    				("LNR_DARLEHEN")->AsInteger;
    
    			/* Typ auf Eroeffnung setzen */
    			typ = "Eröffnung";
    
    			// Datum fuer Buchung abfragen
    			f_darlehen_buchungsdatum->AdvSmoothDatePicker1->Date = DateToStr
    				(Date());
    			f_darlehen_buchungsdatum->ShowModal();
    			buchungsdatum =
    				f_darlehen_buchungsdatum->AdvSmoothDatePicker1->Date;
    
    			// Betrag fuer Buchung abfragen
    			try {
    				buchung = InputBox("Buchung " +
    					(f_start->ADOQuery1_Kundenverwaltung->FieldByName
    						("AUFSTELLPLATZ")->AsString), "Betrag", "0,00");
    			}
    
    			catch(EConvertError & E) {
    				MessageDlg("Ungültige Eingabe!", mtError,
    					TMsgDlgButtons() << mbOK, 0);
    				buchung = InputBox("Buchung " +
    					(f_start->ADOQuery1_Kundenverwaltung->FieldByName
    						("AUFSTELLPLATZ")->AsString), "Betrag", "0,00");
    			}
    
    			// Pruefen ob Daten zum buchen vorliegen
    			if (buchung > 0) {
    				// Tabelle Darlehen zum schreiben oeffnen
    				f_start->ADOTable1_Darlehen_Table->Insert();
    				f_start->ADOTable1_Darlehen_Table->FieldByName("NR_KD")
    					->AsInteger =
    					f_start->ADOQuery1_Kundenverwaltung->FieldByName("NR_KD")
    					->AsInteger;
    				f_start->ADOTable1_Darlehen_Table->FieldByName("BUCHUNGSDATUM")
    					->AsDateTime = buchungsdatum;
    				f_start->ADOTable1_Darlehen_Table->FieldByName("BETRAG")
    					->AsCurrency = buchung;
    				f_start->ADOTable1_Darlehen_Table->FieldByName("SALDO")
    					->AsCurrency = buchung;
    				f_start->ADOTable1_Darlehen_Table->FieldByName("TYP")
    					->AsString = typ;
    				f_start->ADOTable1_Darlehen_Table->FieldByName("AUFSTELLPLATZ")
    					->AsString =
    					f_start->ADOQuery1_Kundenverwaltung->FieldByName
    					("AUFSTELLPLATZ")->AsString;
    				f_start->ADOTable1_Darlehen_Table->FieldByName("BEARBEITER")
    					->AsString = f_start->StatusBar1->Panels->Items[1]->Text;
    				f_start->ADOTable1_Darlehen_Table->Post();
    
    				// Tabelle Kundenverwaltung zum schreiben oeffnen
    				f_start->ADOTable1_Kundenverwaltung->Edit();
    				f_start->ADOTable1_Kundenverwaltung->FieldByName("DARLEHEN")
    					->AsCurrency = buchung;
    				f_start->ADOTable1_Kundenverwaltung->FieldByName("DARLEHEN_DAT")
    					->AsDateTime = DateToStr(Date());
    				f_start->ADOTable1_Kundenverwaltung->FieldByName("DARLEHEN_BET")
    					->AsCurrency = buchung;
    				f_start->ADOTable1_Kundenverwaltung->FieldByName("LNR_DARLEHEN")
    					->AsInteger = lnr_darlehen;
    				f_start->ADOTable1_Kundenverwaltung->Post();
    
    			}
    		}
    	}
    
    	else {
    		// Pruefen ob ein Darlehen gebucht werden soll
    		int dar = MessageDlg("Möchten Sie für den Aufstellplatz '" +
    			f_start->ADOQuery1_Kundenverwaltung->FieldByName("AUFSTELLPLATZ")
    			->AsString + "' etwas buchen?", mtConfirmation,
    			TMsgDlgButtons() << mbYes << mbNo, 0);
    		if (dar == mrYes) {
    
    			// Abfragen ob Typ Darlehen oder Tilgung ist
    			switch(AdvSmoothMessageDialog1->ExecuteDialog()) {
    			case mrYes:
    				/* Typ ist Darlehen */
    				typ = "Darlehen";
    
    				break;
    
    			case mrNo:
    				/* Typ ist Tilgung */
    				typ = "Tilgung";
    
    				break;
    			}
    
    			// Datum fuer Buchung abfragen
    			f_darlehen_buchungsdatum->ShowModal();
    			buchungsdatum =
    				f_darlehen_buchungsdatum->AdvSmoothDatePicker1->Date;
    		}
    
    		// Betrag fuer Buchung abfragen
    		try {
    			buchung = InputBox
    				("Buchung " +
    				(f_start->ADOQuery1_Kundenverwaltung->FieldByName
    					("AUFSTELLPLATZ")->AsString), "Betrag", "0,00");
    			// Pruefen ob Typ Tilgung ist, wenn Ja in Minusbetrag wandeln
    			if (typ == "Tilgung") {
    				buchung = buchung*-1;
    			}
    		}
    
    		catch(EConvertError & E) {
    			MessageDlg("Ungültige Eingabe!", mtError, TMsgDlgButtons() << mbOK,
    				0);
    			buchung = InputBox
    				("Buchung " +
    				(f_start->ADOQuery1_Kundenverwaltung->FieldByName
    					("AUFSTELLPLATZ")->AsString), "Betrag", "0,00");
    		}
    		// Saldo berechnen
    		f_start->ADOQuery1_Darlehen->Last();
    		saldo = f_start->ADOQuery1_Darlehen->FieldByName("SALDO")
    			->AsCurrency + buchung;
    
    		// Pruefen ob Daten zum buchen vorliegen
    		if (buchung < 0 || buchung > 0) {
    			// Tabelle Darlehen zum schreiben oeffnen
    			f_start->ADOTable1_Darlehen_Table->Insert();
    			f_start->ADOTable1_Darlehen_Table->FieldByName("NR_KD")
    				->AsInteger = f_start->ADOQuery1_Kundenverwaltung->FieldByName
    				("NR_KD")->AsInteger;
    			f_start->ADOTable1_Darlehen_Table->FieldByName("BUCHUNGSDATUM")
    				->AsDateTime = buchungsdatum;
    			f_start->ADOTable1_Darlehen_Table->FieldByName("BETRAG")
    				->AsCurrency = buchung;
    			f_start->ADOTable1_Darlehen_Table->FieldByName("SALDO")
    				->AsCurrency = saldo;
    			f_start->ADOTable1_Darlehen_Table->FieldByName("TYP")
    				->AsString = typ;
    			f_start->ADOTable1_Darlehen_Table->FieldByName("AUFSTELLPLATZ")
    				->AsString = f_start->ADOQuery1_Kundenverwaltung->FieldByName
    				("AUFSTELLPLATZ")->AsString;
    			f_start->ADOTable1_Darlehen_Table->FieldByName("BEARBEITER")
    				->AsString = f_start->StatusBar1->Panels->Items[1]->Text;
    			f_start->ADOTable1_Darlehen_Table->Post();
    			f_start->ADOQuery1_Darlehen->Active = false;
    			f_start->ADOQuery1_Darlehen->Active = true;
    			f_start->ADOQuery1_Darlehen->Last();
    		}
    	}
    	// Ansicht fuer zugeordnete Darlehen aktualisieren
    	AnsiString kunde = f_start->ADOQuery1_Kundenverwaltung->FieldByName("NR_KD")
    		->AsString;
    	f_start->ADOQuery1_Darlehen->SQL->Clear();
    	f_start->ADOQuery1_Darlehen->SQL->Add(
    		"SELECT * FROM automatenverwaltung.darlehen");
    	f_start->ADOQuery1_Darlehen->SQL->Add
    		("  WHERE (NR_KD = " + AnsiString(kunde) + ")");
    	f_start->ADOQuery1_Darlehen->SQL->Add("	ORDER BY BUCHUNGSDATUM");
    	f_start->ADOQuery1_Darlehen->Open();
    
    	// ADOTable in Kundenuebersicht aktualisieren
    	f_kundenverwaltung->ADOTable1->Active = false;
    	f_kundenverwaltung->ADOTable1->Active = true;
    
    	// Saldo neu berechen
    	for (int i = 0; i < 10; i++) {
    		AnsiString Kundennummer =
    			f_start->ADOQuery1_Kundenverwaltung->FieldByName("NR_KD")
    			->AsString;
    		AnsiString Nummer;
    		Currency alter_betrag; // Variable fuer alten Betrag
    		Currency alter_saldo; // Variable fuer alten Saldo
    		Currency neuer_saldo; // Variable fuer neuen Saldo
    		f_start->ADOQuery1_Darlehen->First();
    		// An den ersten Datensatz der Tabelle springen
    
    		for (int i = 1; i < f_start->ADOQuery1_Darlehen->RecordCount; i++) {
    			alter_saldo = f_start->ADOQuery1_Darlehen->FieldByName("SALDO")
    				->AsCurrency; // Alten Saldo merken
    			f_start->ADOQuery1_Darlehen->Next();
    			// Einen Datensatz vorspringen
    			Nummer = f_start->ADOQuery1_Darlehen->FieldByName("NUMMER")
    				->AsString; // Nummer von Datensatz uebergeben
    			alter_betrag = f_start->ADOQuery1_Darlehen->FieldByName("BETRAG")
    				->AsCurrency; // Alten Betrag merken
    
    			neuer_saldo = alter_saldo + alter_betrag;
    			// Neuen Saldo berechnen
    			// Neuen Saldo in Tabelle schreiben
    			ADOQuery1->SQL->Clear();
    			ADOQuery1->SQL->Text =
    				"UPDATE automatenverwaltung.darlehen SET SALDO = " +
    				neuer_saldo + " WHERE NR_KD = '" + AnsiString
    				(Kundennummer) + "' AND NUMMER = '" + AnsiString(Nummer) + "'";
    			ADOQuery1->ExecSQL();
    		}
    	}
    	ADOQuery2->Active = false;
    	ADOQuery2->Active = true;
    	f_start->ADOQuery1_Darlehen->Active = false;
    	f_start->ADOQuery1_Darlehen->Active = true;
    	f_start->ADOQuery1_Darlehen->Last();
    

    Für Hilfe bin ich dankbar. LG Maik



  • Bevor ich mich da durch quäle... 😉
    Welche Art von Rechenfehler tritt auf? So weit ich das auf den ersten Blick sehe, rechnest Du alles im Code. Wenn die Werte und / oder Abweichungen sehr klein sind, können das durchaus auch einfach die üblichen Probleme mit Fließkommavariablen sein.



  • Danke für deine Antwort.

    Vielleicht sollten wir mal von einer anderen Seite an die Lösung gehen.

    Angenommen die Tabelle hat folgende Datensätze:

    Datum         Beschreibung    Betrag      Saldo
    
    01.06.2010    Darlehen        100.00 €    -100.00 €
    05.06.2010    Tilgung          50.00 €     -50.00 €
    

    Ich füge bis jetzt den Datensatz immer so ein, dass ich einfach den Saldo des letzten Datensatzes nehm und dann mit dem Betrag des neuen Datensatzes verrechne. Das klappt auch ohne Probleme.

    Nur jetzt muss ich z. B. eine Buchung vom 03.06.2010 einfügen weil diese vergessen wurde. Jetzt fangen die Probleme an weil ich zum einen nicht einfach den Saldo des letzten Datensatzes nehmen kann weil alle anderen danach falsch wären. Deshalb suche ich nach einer Lösung dies z.B. per SQL zu rechnen. Leider fehlt mir hier der Ansatz...

    Falls jemand eine Idee hat wäre ich dankbar.

    LG Maik



  • bist du deine Schleife mal mit dem Debugger durchgegangen und hast dir die Werte für cSaldoAlt, cSaldoNeu, cBetragAlt und iDatensatznummer mal angeschaut?

    stimmt die Sortierung nach dem Buchungsdatum? eine Sortierung nach Lfd Nummer wäre hier falsch

    was auch recht unschön ist:

    ADOQuery1->SQL->Text =
    "UPDATE automatenverwaltung.darlehen SET SALDO = " +
    cSaldoNeu + " WHERE NR_KD = '" + IntToStr(iKundennummer)
    + "' AND NUMMER = '" + IntToStr(iDatensatznummer) + "'";
    ADOQuery1->ExecSQL();
    

    besser wäre hier die Verwendung von Parametern, da die den Typ automatisch setzen, z.B:

    ADOQuery1->SQL->Text = "UPDATE automatenverwaltung.darlehen SET SALDO = :cSaldoNeu \
    WHERE NR_KD = :iKundennummer AND NUMMER = :iDatensatznummer";
    ADOQuery1->Parameter->ParamByName("cSaldoNeu")->Value = cSaldoNeu;
    ADOQuery1->Parameter->ParamByName("iKundennummer")->Value = iKundennummer;
    ADOQuery1->Parameter->ParamByName("iDatensatznumme")->Value = iDatensatznumme;
    ADOQuery1->ExecSQL();
    

    bei deiner Lösung ist z.B. keine Konvertierung von cSaldoNeu dabei. Ist das ein int,double oder currency? werden Kommastellen weggeschnitten?



  • hier mal ne Variante mit SQL

    update automatenverwaltung.darlehen set Saldo = betrag + 
    (select a.saldo from automatenverwaltung.darlehen a where automatenverwaltung.darlehen.buchungsdatum > a.buchungsdatum order by a.buchungsdatum desc limit 1)
    where automatenverwaltung.darlehen.beschr not like 'Darlehen%'
    

    diese hat allerdings 1 Vorraussetzung: beim Eintrag 'Darlehen' muß der Saldo per Hand gesetzt werden, aber das ist ja eh der Startwert

    zu beachten ist auch, dass diese Abfrage so oft ausgeführt werden muß wie Datensätze vorhanden sind, allerdings hauptsächlich bei Datumswerten die eingefügt werden

    und ich weiß nicht ob deine MySQL-Version SubSelects kann 😉



  • Danke für eure Antworten. Ich werde das gleich mal testen in der Hoffnung das es funktioniert. LG Maik



  • das scheint wohl so nicht zu klappen. ich bekomme mit folgendem code:

    ADOQuery4->SQL->Clear();
    			ADOQuery4->SQL->Text = "update automatenverwaltung.darlehen set saldo = betrag + (select a.saldo from automatenverwaltung.darlehen a where automatenverwaltung.darlehen.buchungsdatum > a.buchungsdatum order by a.buchungsdatum desc limit 1) where automatenverwaltung.darlehen.typ not like 'Darlehen%'";
    			ADOQuery4->Open();
    

    nachfolgende MySQL-Fehlermeldung:

    You can't specify target table 'darlehen' for update in FROM clause' aufgetreten.

    Nach dem ich Frau Google gefragt habe hab ich in der MySQL-Referenz gefunden, dass die Kombination von UPDATE und SELECT innerhalb der selben Tabelle nicht erlaubt ist. Leider bekomme ich auch keine Variante mit JOIN ans laufen.

    Falls hier noch mal jemand helfen könnte wäre ich echt sehr dankbar.

    LG Maik


Anmelden zum Antworten