Lars van der Meer: Durchschnittsberechnung aus Datenbankeinträgen läuft schief

Hallo,

ich habe folgendes Problem:

Ich möchte die über einen Zeitraum von mehreren Wochen verteilten durchschnittlichen wöchentlichen Arbeitsstunden in eine Datenbanktabelle schreiben und auch wieder auslesen lassen, und muss da irgendwo einen groben Denkfehler eingebaut haben...

So habe ich für die erste Woche 25 Wochenstunden, für die zweite Woche 19 Wochenstunden eingetragen, was ja eigentlich eine durchschnittliche Wochenstundenanzahl von 22 ergeben müsste... die Datenbank spuckt mir aber 12.5 Stunden aus...?

Ich habe versucht, hier allen das Leben ein wenig leichter zu machen, und den nachfolgenden Code entsprechend auf das [wie ich glaube] Problem reduziert... ...sollte ich dadurch wichtige Informationen vorenthalten, bitte um Hinweis!

Danke für ernstgemeinte Ratschläge.

LG, Lars.


Zum Code:

eingabe.php sieht aus wie folgt:

...IN DER DATENBANK:

Die Tabelle "Stundendaten" enthält als Primärschlüssel "woche", diese ist vom Typ bigint AUTO_INCREMENT - der Primärschlüssel ist also als einfache Durchnummerierung ausgeführt. "wochenstundenanzahl", "gesamtstundenanzahl" sind vom Typ int, "durchschnitt" vom Typ double.

...PHP:

$con = mysqli_connect(...);
$res = mysqli_query($con, "SELECT woche, gesamtstundenanzahl FROM Stundendaten ORDER BY woche DESC LIMIT 1");
		while ($dsatz = mysqli_fetch_assoc($res)) {
			if ($dsatz["woche"] === NULL) {
			$dsatz["woche"] = 1;
			}
			if ($dsatz["gesamtstundenanzahl"] === NULL) {
			$dsatz["gesamtstundenanzahl"] = 0;
			}
			$neue_gesamtstundenanzahl = $dsatz["gesamtstundenanzahl"] + $_POST["wochenstundenanzahl"];
			$aktuelle_Woche = $dsatz["woche"];
			$neuer_durchschnitt = $neue_gesamtstundenanzahl / $aktuelle_Woche;
		}
mysqli_query($con, "INSERT INTO Stundendaten (wochenstundenanzahl, gesamtstundenanzahl, durchschnitt) VALUES ('".$_POST['wochenstundenanzahl']."', '$neue_gesamtstundenanzahl', '$neuer_durchschnitt') ");

(die Wochenstundenanzahl wird via Formular <input name="wochenstundenanzahl" type="number" /> gepostet)

auswertung.php sieht aus wie folgt:

$con = mysqli_connect(...);
$durchschnitt = mysqli_query($con, "SELECT woche, durchschnitt FROM Stundendaten ORDER BY woche DESC LIMIT 1");
	while($dudsatz = mysqli_fetch_assoc($durchschnitt)) {
		if ($dudsatz["woche"] === NULL) {
			exit("<p>Es sind noch keine Einträge vorhanden.</p>");
		}
		echo "<h3>Durchschnittliche Arbeitszeit pro Woche seit Anfang 2016: " . $dudsatz["durchschnitt"] . " Stunden</h3>";
	}
  1. Hallo Lars van der Meer,

    Du holst dir mit LIMIT 1 nur einen Datensatz, deine while-Schleife umfasst also nur einen einzigen Durchlauf.

    Ohnehin kannst du das Berechnen des Durchschnittes auch der Datenbank überlassen. SELECT AVG(Spaltenname) ist dein Freund.

    Bis demnächst
    Matthias

    --
    Das Geheimnis des Könnens liegt im Wollen. (Giuseppe Mazzini)
    1. Hallo,

      danke für die Antwort!

      LIMIT 1 ist aber durchaus beabsichtigt, da ich ja nur den LETZTEN und damit aktuellen (ORDER BY woche DESC) Durchschnittswert ausgeben will... Das müsste meines Erachtens mit meinem Code auch funktionieren; - so werden mit $_POST["wochenstundenzahl"] die Stundenzahlen der aktuellen Woche eingetragen und diese dann zu dem in der Datenbank gespeicherten Datensatz "gesamtstundenanzahl" addiert:

      $neue_gesamtstundenanzahl = $dsatz["gesamtstundenanzahl"] + $_POST["wochenstundenanzahl"];

      Die Variable $neue_gesamtstundenanzahl wird wiederum als "gesamtstundenzahl" in der Datenbank hinterlegt und bei erneutem Aufrufen des Algorithmus abermals zu NEU eingegebenen Stunden addiert, usw.

      Mit $aktuelle_Woche wird dann eben die aktuelle Woche ausgelesen (die via AUTO_INCREMENT automatisch inkrementiert wird), und der Durchschnitt wird dann mit $neuer_durchschnitt = $neue_gesamtstundenanzahl / $aktuelle_Woche; berechnet und auch in die Tabelle eingefügt.


      Dies sähe beispielsweise aus wie folgt:

      Es werden zwei Datensätze eingetragen:

      erste Woche: 25 Wochenstunden, Primärschlüssel: 1 [AUTO_INCREMENT]

      $neue_gesamtstundenanzahl = 0 + 25; $aktuelle_Woche = 1; $neuer_durchschnitt = 25 / 1 /* $neuer_durchschnitt SOLLTE ALSO DEN WERT 25 HABEN! */

      mysqli_query($con, "INSERT INTO Stundendaten (... durchschnitt) VALUES (... '$neuer_durchschnitt') ");


      zweite Woche: 19 Wochenstunden, Primärschlüssel: 2 [AUTO_INCREMENT]

      $neue_gesamtstundenanzahl = 25 + 19; $aktuelle_Woche = 2; $neuer_durchschnitt = 44 / 2 /* $neuer_durchschnitt SOLLTE NUN ALSO DEN WERT 22 HABEN! */

      mysqli_query($con, "INSERT INTO Stundendaten (... durchschnitt) VALUES (... '$neuer_durchschnitt') ");


      ...die Datenbank spuckt mir dagegen statt 22 den Wert 12.5 aus. ...Was läuft da falsch?

      Hallo Lars van der Meer,

      Du holst dir mit LIMIT 1 nur einen Datensatz, deine while-Schleife umfasst also nur einen einzigen Durchlauf.

      Ohnehin kannst du das Berechnen des Durchschnittes auch der Datenbank überlassen. SELECT AVG(Spaltenname) ist dein Freund.

      Bis demnächst
      Matthias

      Das Geheimnis des Könnens liegt im Wollen. (Giuseppe Mazzini)

  2. Tach,

    So habe ich für die erste Woche 25 Wochenstunden, für die zweite Woche 19 Wochenstunden eingetragen, was ja eigentlich eine durchschnittliche Wochenstundenanzahl von 22 ergeben müsste... die Datenbank spuckt mir aber 12.5 Stunden aus...?

    das ergibt sich aus deinem Code nicht; ich würde vermuten, das die Datensätze in deiner Datenbank anders aussehen, als du glaubst.

    <?php
    $con = mysqli_connect(...);
    $res = mysqli_query($con, "SELECT woche, gesamtstundenanzahl FROM Stundendaten ORDER BY woche DESC LIMIT 1");
    		while ($dsatz = mysqli_fetch_assoc($res)) {
    			if ($dsatz["woche"] === NULL) {
    			$dsatz["woche"] = 1;
    			}
    			if ($dsatz["gesamtstundenanzahl"] === NULL) {
    			$dsatz["gesamtstundenanzahl"] = 0;
    			}
    			$neue_gesamtstundenanzahl = $dsatz["gesamtstundenanzahl"] + $_POST["wochenstundenanzahl"];
    			$aktuelle_Woche = $dsatz["woche"];
    			$neuer_durchschnitt = $neue_gesamtstundenanzahl / $aktuelle_Woche;
    		}
    mysqli_query($con, "INSERT INTO Stundendaten (wochenstundenanzahl, gesamtstundenanzahl, durchschnitt) VALUES ('".$_POST['wochenstundenanzahl']."', '$neue_gesamtstundenanzahl', '$neuer_durchschnitt') ");
    
    1. Der Durchschnitt hat in der Datenbank nichts zu suchen, den kann die Datenbank beim Abfragen berechnen.
    2. Die Gesamtstundenzahl hat in der Datenbank nichts zu suchen, den kann die Datenbank beim Abfragen berechnen.
    3. Du vergisst die kontextgerechte Behandlung der Werte, die du aus dem Request übernimmst; ich sehe hier zwar keinen direkten Exploit, aber das heißt nicht, dass es keinen gibt und selbst wenn es keinen gibt, sollte man sich angewöhnen es immer zu tun, dann kann man sich bei der Einschätzung, ob es einen gibt nie irren.
    4. Du verläßt dich darauf dass deine IDs fortlaufend lückenlos sind, das ist keine gute Idee auf längere Sicht; IDs sind ausschließlich dazu da einen Datensatz zu identifizieren. Wenn du zählen willst wie viele Wochen eingetragen sind, kann das die Datenbank für dich erledigen.
    5. Die Fehlerbehandlung beim Eintragen fehlt, aber ich vermute mal, die hast du wegreduziert. In der geposteten Version würde der Insert ausgeführt werden, obwohl der vorherige Select fehlgeschlagen ist.
    6. Warum ist da eine Schleife, wenn du doch weißt, dass es nur maximal ein Ergebnis geben kann?
    7. Du trägst deine Zahlenwerte als Strings in die Datenbank ein.

    1. 2. und 4. in einer Abfrage zusammen sollte so aussehen:

    SELECT COUNT(id) AS wochenanzahl, SUM(wochenstundenanzahl) AS wochenstundenanzahl_gesamt, AVG(wochenstundenanzahl) AS durchschnitt_wochenstundenanzahl FROM Stundendaten;
    

    mfg
    Woodfighter