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