gechger
Goto Top

PHP während einer while Schleife Zwischensumme bilden

Eine mysql Abfrage liefert mir verschiedene Benutzer mit verschiedenen Daten (hier explizit Zeiten). Wenn ein neuer Benutzer in der while Schleife erkannt wird, soll eine Zwischensumme seiner Zeiten ausgegeben werde. Wie stele ich das am geschicktesten an?

Hallo Forum,

eine Monatsabfrage einer Mysql Datenbank liefert mir nach Person sortiert eine Menge Daten, die ich in einer while Schleife ausgebe.

Sie sieht vereinfacht so aus:
MA1, 01.09.2009, 8 Stunden
MA1, 02.09.2009, 6,5 Stunden
MA2, 01.09.2009, 4 Stunden
MA2, 02,09,2009, 6,25 Stunden
MA3,...
MA3,...
MA4,...
MA4,...

usw.

Wenn in der while Schleife von MA1 auf MA2 gewechselt wird, sollen alle Zeiten des MA1 summiert in einer Zwischensumme ausgegeben werden. Am Ende sollen alle Zwischensummen als Gesamtssumme des ausgewählten Zeitraumes erscheinen.

Wie stelle ich das am geschicktesten an?

Vielen Dank für jede Idee
Schöne Grüße
Christof

Content-Key: 124457

Url: https://administrator.de/contentid/124457

Printed on: April 16, 2024 at 23:04 o'clock

Member: Snowman25
Snowman25 Sep 08, 2009 at 11:26:26 (UTC)
Goto Top
wenn dir die Datenbank schon die Zeiten liefert, dann rechne doch auch mit Ihnen. Sobald du einen MA durch hast, lässt du dir nur die Stunden des MA ausgeben und rechnest die zusammen. Dabei behälst du immer das letzte Ergebnis, gibst es aus und rechnest es dann deiner Gesamtsumme zu. Die gibst du dann ganz am Ende aus. Du brauchst also 2 Schleifen.
Member: gechger
gechger Sep 08, 2009 at 11:57:48 (UTC)
Goto Top
Hallo snowman,

mit dieser Selectabfrage

$select = "SELECT ztyp, zdat, zper, zpro, pzmember.nname, pzmember.vname from pzetool left JOIN pzmember on pzmember.rfid = pzetool.zper where (zpro = $fanr) and (zdat BETWEEN '$datumvon' AND '$datumbis') ORDER BY zper, zdat";

In dem zdat Feld bekomme ich Datum und Uhrzeit.
Durch ein Script ermittle ich eine von Zeit und eine bis Zeit, die ich gegeneiander aufrechnen kann.
Dieses wandle ich dann noch um.

$zdatjahr=substr($zdatsav,0,4);
      $zdatmonat=substr($zdatsav,5,2);
      $zdattag=substr($zdatsav,8,2);
      $zdatstr="$zdattag"."."."$zdatmonat"."."."$zdatjahr";  

      //Aufschlüsselung des gesamten Zeitwertes in die einzelnen Werte
      $jahrv=substr($strtimvsav,0,4);
      $monatv=substr($strtimvsav,5,2);
      $tagv=substr($strtimvsav,8,2);
      $stdv=substr($strtimvsav,11,2);
      $minv=substr($strtimvsav,14,2);
      $sekv=substr($strtimvsav,17,2);

      $jahrb=substr($strtimbsav,0,4);
      $monatb=substr($strtimbsav,5,2);
      $tagb=substr($strtimbsav,8,2);
      $stdb=substr($strtimbsav,11,2);
      $minb=substr($strtimbsav,14,2);
      $sekb=substr($strtimbsav,17,2);

      //Errechnung des UNIX-Timestamp Wertes

      $zeitv=mktime($stdv,$minv,$sekv,$monatv,$tagv,$jahrv);
      $zeitb=mktime($stdb,$minb,$sekb,$monatb,$tagb,$jahrb);
      $unixzeit=$zeitb-$zeitv;

      $unixzeit2=$strtimbsav-$strtimvsav;

      $zeit=gmdate("H:i:s",$unixzeit);  

In $zeit steht also nun die errechnete Anwesenheitszeit.

Die Select Abfrage kann mir bis zu 120 Mitarbeiter ausgeben, die alle mit Zwischensummen voneinander getrennt werden sollen.

Meine Idee geht in die Richtung:
zper=Personalnummer
zpertemp=temporäre Variable

Vor der while Schleife:
$zpertemp = "";

Durchlauf der Schleife:
if ($zpertemp=0)
{
$zpertemp=$zper; 
$zeittemp=+$zeit; //Hier sollte solange der Name gleich ist, die Zeit bei jedem Durchlauf in eine temp Variable hinzugefügt werden

}
if ($zpertemp !=$zper)
$zwsumme=$zeittemp;
$zeittemp=0; //Wenn ein Wechsel erkannt ist, soll die aufgelaufene Summe weg geschrieben und ausgegeben werden, die temp Variable wird auf null zurückgesetzt
{

Irgendwie so hatte ich das angedacht, aber geht das überhaupt?

Mit einer zweiten Schleife arbeiten scheint mir da wesentlich komplizierter zu sein.

Schöne Grüße
Christof
Member: Snowman25
Snowman25 Sep 08, 2009 at 12:25:10 (UTC)
Goto Top
nunja, ich dachte mir das so, dass du eine äussere schleife hast, die die MA's durchläuft und die Innere jeweils die einzelnen Einträge für den momentanen MA.
In der Inneren Schleife gibst du einfach die Einträge aus und am Ende jeder Ausgabe (bzw. innerem schleifendurchlauf) addierst du die MA Zeiten aufeinander ($zwsumme+=$zeittemp).
Sobald du einen MA durch hast, also am Fuß der äusseren Schleife bist, gibst du $zwsumme aus, rechnest $zwsumme dann zu $gesamtzeit hinzu und nullst $zwsumme dann wieder.
kurz:
while  ... { //äussere Schleife
      $zper=<MA-Nummer>;
      for ($i = 1; $i <= <Anzahl Listeneinträge>; $i++) { //Innere Schleife
            Echo <Eintrag Nr. $i von $zper>;  //Ausgabe Listeneintrag
            $zeittemp=<Zeit von Eintrag $i von MA $zper>;
            $zwsumme+=$zeittemp;
      }
      Echo "$zwsumme";  
      $gesamtzeit+=$zwsumme;
      $zwsumme=0;
}

stark vereinfacht... aber das Prinzip sollte klar sein
hoffe das hilft

Snow
sollte mal wieder php üben...
Member: kopie0123
kopie0123 Sep 08, 2009 at 12:33:52 (UTC)
Goto Top
Ich würde die gesamte Rechnenlogik in Datenbankabfrage verlagern. Schau dir mal GROUP BY an, damit sollte man es auch lösen können.

Oder du lässt die erst alle MAs ausgeben und fragst dann alle Datensätze der jeweiligen MAs ab. Das sollte schneller sein, als das Schleifen-gemauschel face-smile

Gruß
Member: godlie
godlie Sep 08, 2009 at 14:58:23 (UTC)
Goto Top
Hallo,

naja ich würd mir das ganze recht elegant basteln.
Nachdem in $zper die personalnummer drinne ist und dich ja eigentlich nur die Gesamtsumme interessiert,
würde ich das so angehen:
$stunden = array();
while(  mysql_fetch.... ) {

if(array_key_exists($zper, $stunden))
$stunden[$zper] = $stunden[$zper]+$zeit;
else
$stunden[$zper] = $zeit;
}

das wäre so meien Idee grob umrissen. Ich glaub du wirst da noch ein paar Umwandlungen brauchen aber sonst dürft das so funktionieren wie ich das Verstanden habe.

@stingermac
Ich glaube das mit dem Berechnungen in der DB wird nciht hinhauen wenn ers so zerlegen muss.
Member: nxclass
nxclass Sep 09, 2009 at 07:02:43 (UTC)
Goto Top
wow! - also das Datum kannst du Dir auch gleich in der SQL Abfrage umwandeln.
http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html

SELECT
  ...
SEC_TO_TIME( MAX(UNIX_TIMESTAMP(zdat)) - MIN(UNIX_TIMESTAMP(zdat)) ) AS 'zeit'  
// ggf geht auch: SEC_TO_TIME( UNIX_TIMESTAMP(MAX(zdat)) - MIN(zdat)) ) AS 'zeit'  
  ...
GROUP BY
   DATE( zdat )
..hoffe ich habe das so richtig verstanden wie die Zeiten gespeichert werden !?

Zwischensummen:
Man könnte eine Temporäre MYSQL Tabelle erstellen, diese mit den einzelnen Einträgen (s.o.) füllen und anschließend noch mit den Zwischensummen - oder auch gleich eine UNION Abfrage generieren.
Member: gechger
gechger Sep 09, 2009 at 20:51:35 (UTC)
Goto Top
Hallo an Alle,

es ist ja Wahnsinn, welche Lösungsansätze man wählen kann.

Erstmal @nxclass:
Die User nutzen ein Zeiterfassungsgerät, welches die Zeiten per GSM an die MySQl Datenbank sendet. Morgens einstempeln ergibt einen Datensatz, abends ausstempeln einen weiteren. Ich habe in der DB dann stehen:
Personalnummer 1234, Anmeldung 07:15
Personalnummer 5688, Anmeldung 07:30
usw
Personalnummer 1234, Abmeldung 16:15
Personalnummer 5688, Abmeldung 16:30
usw

Um die Zeiten eines Users berechnen zu können, erfolgt das order by Kriterium nach Personalnummer und Zeit, also zper und zdat.
In einer SQl Abfrage müßte ich dann doch erst die praktisch unsortierten Datensätze ersteinmal vorsortieren, damit die Berechnung 'bis-Zeit' - 'von-Zeit' zu dem richtigen User erfolgt. Ich denke, daß es einfacher ist, die vorsortierten Datensätze in php weiter zu verarbeiten.
Außerdem besteht mein SQL Datensatz in der Form: DD.MM.YYYY HH:MM:SS.
Das Datum muß ich erst ausfiltern, um es in einem Formular verwenden zu können, das erlaubt, den gewünschten Zeitraum von-bis auszuwählen. Die Zeiten benötige ich, um die Arbeitszeit des Tages zu errechnen, und anschließend die Zwischensumme zu bilden über den ausgewählten Zeitraum. Deshalb die aufwendige substr Struktur.


@godlie
Dein Ansatz scheint mir irgendwie erfolgversprechend. Ich habe es zwar noch nicht ganz durchschaut, aber das probiere ich mal aus. Ich habe zwischenzeitlich auchschon was probiert, was halbwegs funktioniert, aber eben nicht so ganz richtig.
Ich habe mit einer Tempvariablen gearbeitet. Vor der Schleife definiere ich diese als leer.
Dann kommt die Schleife, wobei ich prüfe:
        $unixzeit=$zeitb-$zeitv;
        $zeit=gmdate("H:i:s",$unixzeit);  

        $zwsummead+=$unixzeit;
        if (empty($zpertemp))
           {
           $zpertemp=$zper;
            }

        if ($zpertemp==$zpersav)
           {
           $zpertemp=$zper;
           }

        if ($zpertemp != $zpersav)
           {
           $zwsumme=$zwsummead;
           $zwsummead="";  
           $zpertemp=$zper;
           continue;
           }

           include"ausgabedat.php";  
         }
Die Zwischensumme wird mir korrekt ausgegeben, aber sie steht nicht unter dem User, sondern immer eine Zeile weiter, nämlich erst dann, wenn $zpertemp != $zpersav erkannt wurde. Der erste Datensatz des neuen Users wird ausgegeben, dann kommt die Zwischensumme. Ich denke, mit Deinem Ansatz komme ich weiter.

@Snowman
Dein Hinweis auf die Möglichkeit += zu verwenden, hat mich bereits einen Riesenschritt weiter gebracht. Da mich mein eigener Ansatz noch nicht weiter gebracht hat, werde ich auch die gestaffelte Schleife mal durch testen.

Ergebnisse werde ich kurzfristig nicht haben, da mich meine normalen Supportaufgaben wieder bundesweit auf deutsche Autobahnen führen werden, und ich keine Zeit habe, mich sofort an mein php Script zu stürzen. Aber sobald wieder etwas Luft ist.......

Euch allen erstmal herzlichen Dank für alle Ansätze.

Schöne Grüße
Christof
Member: Guenni
Guenni Sep 14, 2009 at 14:10:14 (UTC)
Goto Top
Hi Christof,

diese Strategie . . .

Zitat von @gechger:

. . . Morgens einstempeln ergibt einen Datensatz,

abends ausstempeln einen weiteren. . . .


. . . würde ich mal überdenken. Wenn du Einfluß darauf hast, würde ich

Ein- und Ausstempeln in einen Datensatz packen/packen lassen.


Dann ist das Summieren von Zeiten ohne if und else und temporären Variablen

in ein paar Zeilen erledigt.


Dein Code, um Datum und Zeit(en) aus dem Feld zdat zu filtern, ist auch nicht

notwendig, da MySQL Funktionen bereitstellt, die mit dem Datentyp datetime umgehen können:

- DATE_FORMAT(Spaltenname,"%Y-%m-%d") oder DATE_FORMAT(Spaltenname,"%d.%m.%Y") - Datum

- TIME_TO_SEC(Spaltenname) - Uhrzeit in Sekunden


$query_sum = 'select id,sum((time_to_sec(ende)-time_to_sec(beginn))/60/60) as ZwiSum'  
            . ' from tabelle2 join zeiten on tabelle2.id=zeiten.u_id'  
	    . ' where datum between "'.$vonTag.'" and "'.$bisTag.'"'  
	    . ' group by id';  

$result_sum = mysql_query($query_sum);


So hättest du alle Stunden/MA in ZwiSum abgelegt(Datum hab ich bei mir in einer sep. Spalte).

Jetzt wird das Ergebnis ausgewertet:

while($row_sum = mysql_fetch_array($result_sum,MYSQL_ASSOC)){
 
 $id=$row_sum['id'];  

 /*
 Innerhalb dieser Schleife erfolgt eine Abfrage der Zeiten nach id, gruppiert wird wie oben

 nach id, zusätzlich noch nach Datum.  
 */

 $sql = 'SELECT id,z_id,vorname,nachname,datum,beginn,ende,sum((time_to_sec(ende)-time_to_sec(beginn))/60/60) as summe from zeiten'  
        . ' join tabelle2 on tabelle2.id=zeiten.u_id'  
	. ' where datum between "'.$vonTag.'" and "'.$bisTag.'"'  
	. ' and id = "'.$id.'"'  
	. ' group by id,datum';  

 $result = mysql_query($sql);

 while($row=mysql_fetch_array($result,MYSQL_ASSOC)){
   
  //Alle Stunden zusammenaddieren
  
  $gesamtsumme+=$row['summe'];  
  
  //MA + Datum + Beginn/Ende + Std/Tag ausgeben
	
  echo implode(" | ",$row)."<br>";  
 
 }// while2 Ende
 
 /*
 Nach Ausgabe der einzelnen Tage die Zwischensummme Std/MA ausgeben,
 
 die in der ersten Abfrage ermittelt wurde
 */
 
 echo "Zwischensumme/MA: $row_sum[ZwiSum]<br><br>";  

}// while1 Ende

//Zum Schluß Gesamtstunden aller MA ausgeben

echo $gesamtsumme;


Gruß
Günni
Member: gechger
gechger Sep 14, 2009 at 20:00:00 (UTC)
Goto Top
Grüß Dich Günni,

die Datensätze können leider nicht direkt zusammengefasst werden. Die elektronischen Geräte, die verwendet werden, senden die Datensätze unmittelbar nach deren Erfassung in eine Mysql Datenbank. Das Zusammenfassen habe ich bereits gelöst.

Durch die order by Ausgabe nach Personalnummer und Zeit bekomme ich die Daten in der richtigen Reihenfolge ausgegeben.
Der "Start" Satz wird in einer temporären Variablen zwischen gespeichert und erst ausgegeben, wenn der passende "Ende" Satz auch vorhanden ist. Die Ausgabe erfolgt dann in der Form:

Datum, Mitarbeiter, Zeit von, Zeit bis, Differenz Zeit bis minus Zeit von.
Das funktioniert bereits sehr gut. Zwischensummen bekomme ich dank der Hilfe des Forums auch schon, nur nicht an der Stelle, wo ich sie ausgegeben haben möchte.

Zur Veranschauchlichung der bisherige Code im Teststadium:
<?
//Festlegen der Temporären Variablen
$ztypsav="";  
$zdatsav="";  
$zstpsav="";  
$ztivsav="";  
$ztibsav="";  
$zpersav="";  
$zprosav="";  
$nnamesav="";  
$vnamesav="";  
$lasttyp="";  
$lastper="";  
$laststp="";  
$strtimvsav="";  
$strtimbsav="";  
$zpertemp="";  
$zwsummead="";  
$zwsumme="";  
$p="";  
$p2="";  
$zahl="";  

if (isset($_POST["submit"]))  
   {

   @$datumvon=$_POST["jahrvon"]."-".$_POST["monatvon"]."-".$_POST["tagvon"]." "."00:00:00";  
   @$datumbis=$_POST["jahrbis"]."-".$_POST["monatbis"]."-".$_POST["tagbis"]." "."23:59:59";  
   @$namecheck=$_POST["nachname"];  

   $cldb = mysql_connect($_SESSION['host'], $_SESSION['dbuser'], $_SESSION['passwort']) or die(mysql_error());  
   mysql_select_db($_SESSION['db']);  

   $select = "SELECT ztyp, zdat, zper, zpro, pzmember.nname, pzmember.vname  from pzetool left JOIN pzmember on pzmember.rfid = pzetool.zper where (zpro = $fanr) and (zdat BETWEEN '$datumvon' AND '$datumbis') ORDER BY zper, zdat";  
   $result=mysql_query($select);
   echo mysql_error();


// Erstellung einer Datei mit den Werten des Arrays
   $anmeldedat=$_SESSION['logindat'];  
   $daten=$anmeldedat.".csv";  
   $datei=fopen($daten,"w");  
   $_SESSION['dateiname']=$daten;  



   $i=0;
   //$stunden = array();

   while($ausgabe = mysql_fetch_row($result))  //Array der Abfrage in Variablen speichern
      {

      $ztyp=$ausgabe;
      $zstp=$ausgabe[1];
      $zdat=substr($zstp,0,10);
      $ztim=substr($zstp,11,8);
      $zper=$ausgabe[2];
      $zpro=$ausgabe[3];
      $nname=$ausgabe[4];
      $vname=$ausgabe[5];




//Prüfung, ob temporäre Variablen gefüllt sind: Datum, Personalnummer und Typ. Wenn ja, wird Schleife abgebrochen
      if (($zstp==$laststp) && ($zper==$lastper) && ($ztyp==$lasttyp))
         {
         continue;
         }
// Wenn TempVAR Typ leer ist: Ausgabe nein, Füllen der TempVAR ja.
      if (empty($ztypsav))
         {
         $liste="0";  
         $kopieren="1";  
         }
//Wenn TempVAR Typ gefüllt ist, wird ausgegeben und weiter geprüft.
      else
         {
         $liste="1";  
//Erste Prüfung auf Typ: es steht ST drin
         if ($ztypsav=="ST")  
            {
//Zweite Prüfung auf Typ: es steht EN drin
            if ($ztyp=="EN")  
               {
//Wenn EN drin steht, wird dir TemVAR Zeit bis mit der ermittelten Uhrzeit gefüllt, aber nicht weg geschrieben
               $ztibsav=$ztim;
               $strtimbsav=$zstp;
               $kopieren="0";  
               }
//Wenn ST drin steht, wird die TempVAR gefüllt
            else
               {
               $kopieren="1";  
               }
            }
//Wenn TempVAR Typ mit EN gefüllt ist, und kein ST vorliegt,wird die von Zeit mit 00:00:00 gefüllt und weg geschrieben
         else
            {
            $ztivsav="00:00:00";  
            $kopieren="1";  
            }
         }
//Wenn Liste schreiben aktiviert ist, wird die ausgabedat aufgerufen und ausgeführt
      if ($liste=="1")  
         {
         $i++;
         if ((empty($vnamesav)) && (empty($nnamesav)))
         {
         $nnamesav=$zper;
         }

        //Umwandlung des vorgegebenen Formates in deutschen Standard
        $zdatjahr=substr($zdatsav,0,4);
        $zdatmonat=substr($zdatsav,5,2);
        $zdattag=substr($zdatsav,8,2);
        $zdatstr="$zdattag"."."."$zdatmonat"."."."$zdatjahr";  

        //Aufschlüsselung des gesamten Zeitwertes in die einzelnen Werte
        $jahrv=substr($strtimvsav,0,4);
        $monatv=substr($strtimvsav,5,2);
        $tagv=substr($strtimvsav,8,2);
        $stdv=substr($strtimvsav,11,2);
        $minv=substr($strtimvsav,14,2);
        $sekv=substr($strtimvsav,17,2);

        $jahrb=substr($strtimbsav,0,4);
        $monatb=substr($strtimbsav,5,2);
        $tagb=substr($strtimbsav,8,2);
        $stdb=substr($strtimbsav,11,2);
        $minb=substr($strtimbsav,14,2);
        $sekb=substr($strtimbsav,17,2);

        //Errechnung des UNIX-Timestamp Wertes

        $zeitv=mktime($stdv,$minv,$sekv,$monatv,$tagv,$jahrv);
        $zeitb=mktime($stdb,$minb,$sekb,$monatb,$tagb,$jahrb);
        $unixzeit=$zeitb-$zeitv;
        $unixzeit2=$strtimbsav-$strtimvsav;
        $zeit=gmdate("H:i:s",$unixzeit);  

//Beginn der Zwischensumme

        if (empty($zpertemp))
           {
           $zpertemp=$zper;
           }

        if ($zpertemp==$zper)
        	  {
           $zwsummead+=$unixzeit;
           $p2++;
           //echo "P2=$p2","<br>"; 
           }
        if ($p != $p2)
           	{
           	$zwsumme=$zwsummead;
            //$_SESSION['zwsum']=$zwsumme; 
            }

        if ($zpertemp != $lastper)
           {
           $zwsummead="";  
           $zpertemp="";  
           $zwsumme="";  
           $p="";  
           $p2="";  
           }

        include"ausgabedat.php";  

         }
//Wenn kopieren auf Null steht, werden die TempVars gelöscht
         if ($kopieren=="0")  
            {
            $ztypsav="";  
            $zdatsav="";  
            $zstpsav="";  
            $ztivsav="";  
            $ztibsav="";  
            $zpersav="";  
            $zprosav="";  
            $nnamesav="";  
            $vnamesav="";  
            $zstpstrv="";  
            $zstpstrb="";  

            }
//Ansonsten werden sie gefüllt
         else
            {
            $ztypsav=$ztyp;
            $zdatsav=$zdat;
            $zstpsav=$zstp;
            $zpersav=$zper;
            $zprosav=$zpro;
            $nnamesav=$nname;
            $vnamesav=$vname;



//Wenn der Typ ST lautet, wird die von-Zeit mit der ermittelten Zeit gefüllt, die bis-Zeit wird überschrieben
            if ($ztyp=="ST")  
               {
               $ztivsav=$ztim;
               $ztibsav="";  
               $strtimvsav=$zstp;
               }
//Bei EN wird die von-Zeit gelöscht uns die bis-Zeit gefüllt
            else
               {
               $ztivsav="";  
               $ztibsav=$ztim;
               $strtimbsav=$zstp;
               }
               $p++;
		         //echo "P=$p","<br>"; 
            }
//Speichern des letzten Datensatzes
         $lasttyp=$ztyp;
         $lastper=$zper;
         $laststp=$zstp;

      }

Die VAriablen $p und $p2 sollen mir hoffentlich die Unterscheidung liefern, die ich brauche, um die Zwischensumme sauber zu plazieren. Aber da muß ich noch dran arbeiten.
Außerdem ist der Code noch nicht aufgeräumt. Einige Deklarationen sind noch von vorherigen Tests drin. Ist halt noch eine Beta Version.

Vielen Dank für Deinen Vorschlag.
Bisher habe ich ja viele Deiner Vorschläge erfolgreichumsetzen können.

Schöne Grüße
Christof
Member: Guenni
Guenni Sep 21, 2009 at 12:25:51 (UTC)
Goto Top
Hi Christof,


Zitat von @gechger:
. . . die Datensätze können leider nicht direkt zusammengefasst werden. . . .

Das ist natürlich Pech, wenn die Stempeluhrsoftware das nicht kann. Dein Code zum

Ermitteln von Daten und Zeiten, Zuordnen von Zwischensummen zu ID's mit den endlosen

Variablen, if/else-Konstrukten sowie den substr-Operationen ist mir ehrlich gesagt

ein bißchen zu verworren, will sagen: schwer lesbar.


Ich ziehe es vor, die Daten und Zeiten gleich bei der Abfrage mit Hilfe von MySQL-Funktionen

zu ermitteln. Die MA-ID, das Datum und die Ein/Ausstempelzeit habe ich durch zwei

Abfragen/Schleifen in einem Array als Arrays gespeichert.


Erste Abfrage mit Einstempelzeit:

<?php
/*
Aus dem Feld zeit(Datentyp datetime) wird das Datum extrahiert sowie die Uhrzeit in Sekunden.
Die Funktion min ermittelt den kleineren Zeitwert als Beginn
*/
$query='select u_id,date_format(zeit,"%d.%m.%Y") as Datum,min(time_to_sec(zeit)) as Beginn'  
						 .' from zeiten2 where date_format(zeit,"%Y-%m-%d") between "'.$vonTag.'" and "'.$bisTag.'"'  
						 .' group by u_id,Datum';  
$result=mysql_query($query);
/*
Die Ergebnisse werden als Arrays in einem Array abgelegt. Der Endzeitwert wird in den Arrays auf Null
gesetzt, er wird in einer weiteren Abfrage ermittelt.
*/
while($row=mysql_fetch_array($result,MYSQL_ASSOC)){
 $zeiten = array("ID" => $row['u_id'],"Datum" => $row['Datum'],"Beginn" => $row['Beginn'],"Ende" => 0);  
}
?>

Zweite Abfrage mit Ausstempelzeit:

<?php
/*
Die Abfrage wird erneut abgesetzt, diesmal wird der größere Zeitwert in Sekunden durch die Funktion max
als Ende extrahiert.
*/
$query='select u_id,date_format(zeit,"%d.%m.%Y") as Datum,max(time_to_sec(zeit)) as Ende'  
						 .' from zeiten2 where date_format(zeit,"%Y-%m-%d") between "'.$vonTag.'" and "'.$bisTag.'"'  
						 .' group by u_id,Datum';  
$result=mysql_query($query);
/*
Der Index 'Ende' der Arrays kann nun gesetzt werden. 
*/
$i=0;
while($row=mysql_fetch_array($result,MYSQL_ASSOC)){
 $zeiten[$i][Ende] = $row['Ende'];  
 $i++;
}
?>

In dem Array $zeiten befinden sich nun Arrays, die folg. enthalten:

ID der MA, Arbeitstag, Einstempel-/Ausstempelzeit

Errechnen und Ausgabe der Stunden/MA, Ausgabe der Zwischensummen und der Gesamtstunden erfolgt nach dem

nächsten Zitat face-smile


Zitat von @gechger:
. . . Zwischensummen bekomme ich dank der Hilfe des Forums auch schon,

nur nicht an der Stelle, wo ich sie ausgegeben haben möchte. . . .


Ich vermute mal, du setzt eine Abfrage ab, und willst durch entspr. if/else/then-Kontrollen prüfen, wann

der Wechsel von einer ID zur nächsten ID erfolgt um dann die Zwischensumme auszugeben. Erstens glaube ich nicht,

das das geht, und wenn, dann nur mit erheblichem Aufwand. Da ist es erheblich einfacher, zwei Schleifen zu programmieren,

so wie es Snowman schon geschrieben hat, bzw. wie es in meinem Beispiel steht:

Eine Schleife, die dir das Kriterium liefert, nach dem eine Zusammenfassung erfolgen soll, innerhalb dieser Schleife eine weitere,

die die Zusammenfassung vornimmt. Wenn dann die "innere" Schleife die Zusammenfassung erledigt hat, wird sie beendet und die

Zusammenfassung wird NACH dem Beenden der inneren Schleife ausgegeben. Kurz ausgedrückt:

Die "äußere" Schleife liefert dir einen Wert(z.B. eine ID), die "innere" Schleife findet nur Werte, die zu der ID passen,

die dir die "äußere" Schleife liefert.

Die "innere" Schleife hab ich ja oben schon abgekaspert(Eigentlich waren es ja zwei Schleifen, wegen doppelter Datensätze), und

hab mir daraus ein Array erstellt, mit Arrays, die Werte zu versch. MA haben. Die ID's der MA sind auch in den Arrays vorhanden.

Die Werte der Arrays in diesem Array kann ich nun z.B. mit einer foreach-Schleife abrufen. Jetzt will ich aber, dass mir diese

foreach-Schleife nacheinander nur Werte zusammenfasst, die zu einem bestimmten Wert(einer ID) gehören. Diesen Wert(ID) liefert mir

eine Schleife, in der ich die ID's der MA abrufe:

<?php
/*
Aus der Tabelle der MA werden die ID's abgefragt, die auch in der Tabelle 
Zeiterfassung sind. 
*/
$query='select id from tabelle2'  
			.' join zeiten2 on tabelle2.id=zeiten2.u_id'  
			.' where date_format(zeit,"%Y-%m-%d") between "'.$vonTag.'" and "'.$bisTag.'"'  
			.' group by id';  
$result=mysql_query($query);
?>


<?php
/*
Jetzt werden die zwei Schleifen verschachtelt:
*/
while($row=mysql_fetch_array($result,MYSQL_ASSOC)){
 foreach($zeiten as $zeit){
  if($zeit[ID]==$row['id']){  
	 $std=(($zeit[Ende]-$zeit[Beginn])/60/60);
	 /*
	 Die ID/MA, die Stunden MA/Tag werden ausgegeben
	 */
	 echo $zeit[ID]."-".$zeit[Datum]."-".$std." Std<br>";  
	 /*
	  Stunden der MA summieren
	 */
	 $zwisum+=$std;
	 /*
	  Alle Stunden summieren
	 */
	 $gesamtsumme+=$std;
  } 
 }
 /*
 Summe Stunden/Tage/MA ausgeben, Var. $zwisum auf Null setzen
 **nach** Beenden der "inneren" Schleife 
 */
 echo "Zwischensumme MA/$row[id]: $zwisum Std<br><br>";  
 $zwisum=0;
}
?>

Und wenn die "äußere" Schleife endet, wird die Summe aller Stunden ausgegeben.

<?php
echo $gesamtsumme;
?>


Das Script:


<?php

/*********************************************** Script Zeitauswertung *************************************************/
/*
Datumsbereich
*/
$vonTag="2009-09-01";  
$bisTag="2009-09-30";  
/*
Gesamtstunden
*/
$gesamtsumme=0;
/*
Zwischensummen
*/
$zwisum=0;
/*
Aus dem Feld zeit(Datentyp datetime) wird das Datum extrahiert sowie die Uhrzeit in Sekunden.
Die Funktion min ermittelt den kleineren Zeitwert als Beginn
*/
$query='select u_id,date_format(zeit,"%d.%m.%Y") as Datum,min(time_to_sec(zeit)) as Beginn'  
						 .' from zeiten2 where date_format(zeit,"%Y-%m-%d") between "'.$vonTag.'" and "'.$bisTag.'"'  
						 .' group by u_id,Datum';  
$result=mysql_query($query);
/*
Die Ergebnisse werden als Arrays in einem Array abgelegt. Der Endzeitwert wird in den Arrays auf Null
gesetzt, er wird in einer weiteren Abfrage ermittelt.
*/
while($row=mysql_fetch_array($result,MYSQL_ASSOC)){
 $zeiten = array("ID" => $row['u_id'],"Datum" => $row['Datum'],"Beginn" => $row['Beginn'],"Ende" => 0);  
}
/*
Die Abfrage wird erneut abgesetzt, diesmal wird der größere Zeitwert in Sekunden durch die Funktion max
als Ende extrahiert.
*/
$query='select u_id,date_format(zeit,"%d.%m.%Y") as Datum,max(time_to_sec(zeit)) as Ende'  
						 .' from zeiten2 where date_format(zeit,"%Y-%m-%d") between "'.$vonTag.'" and "'.$bisTag.'"'  
						 .' group by u_id,Datum';  
$result=mysql_query($query);
/*
Der Index 'Ende' der Arrays kann nun gesetzt werden. 
*/
$i=0;
while($row=mysql_fetch_array($result,MYSQL_ASSOC)){
 $zeiten[$i][Ende] = $row['Ende'];  
 $i++;
}
/*
Aus der Tabelle der MA werden die ID's abgefragt, die auch in der Tabelle 
Zeiterfassung sind. 
*/
$query='select id from tabelle2'  
			.' join zeiten2 on tabelle2.id=zeiten2.u_id'  
			.' where date_format(zeit,"%Y-%m-%d") between "'.$vonTag.'" and "'.$bisTag.'"'  
			.' group by id';  
$result=mysql_query($query);
/*
Ausgabe des Erfassungszeitraums.
In der foreach-Schleife werden die ID's in den Arrays mit den ID's aus der letzten 
Abfrage verglichen. Bei Übereinstimmung werden
- die Arbeitsstunden pro Tag berechnet($std)
- die Stunden/MA summiert($zwisum)
- Stunden aller MA summiert($gesamtsumme)
ausgegeben.
*/
echo "Zeiterfassung vom $vonTag bis zum $bisTag<br><br>";  
while($row=mysql_fetch_array($result,MYSQL_ASSOC)){
 foreach($zeiten as $zeit){
  if($zeit[ID]==$row['id']){  
	 $std=(($zeit[Ende]-$zeit[Beginn])/60/60);
	 /*
	 Die ID/MA, die Stunden MA/Tag werden ausgegeben
	 */
	 echo $zeit[ID]."-".$zeit[Datum]."-".$std." Std<br>";  
	 $zwisum+=$std;
	 $gesamtsumme+=$std;
  } 
 }
 /*
 Summe Stunden/Tage/MA ausgeben, Var. $zwisum auf Null setzen
 */
 echo "Zwischensumme MA/$row[id]: $zwisum Std<br><br>";  
 $zwisum=0;
}
/*
Ausgabe Stunden/Aller MA
*/
echo "Stunden aller MA: $gesamtsumme Std<br>";  
/*********************************************** Script Zeitauswertung Ende *********************************************/
?>


Gruß
Günni
Member: gechger
gechger Sep 22, 2009 at 18:19:56 (UTC)
Goto Top
Hallo Günni,

face-smile Du hast Dir ja wieder mal richtig Mühe gegeben. Dafür erstmal vielen Dank.

Du hast auch zuerst mal Recht:

Ermitteln von Daten und Zeiten, Zuordnen von Zwischensummen zu
ID's mit den endlosen

Variablen, if/else-Konstrukten sowie den substr-Operationen ist mir
ehrlich gesagt

ein bißchen zu verworren, will sagen: schwer lesbar.


Leider kann ich auf einen großen Teil der Abfragen nicht verzichten. Zum besseren Verständnis:

Der Mitarbeiter (MA) hält morgens seinen RFID Transponder Chip vor das Gerät und wird angemeldet. In der Datenbank wird dieser Datensatz mit ST für Start registriert. Hält er abends seinen Chip wieder davor, dann wird er abgemeldet, ein Endesatz (EN) wird gespeichert.
Nun kann er das EN aber vergessen. Dann folgt am nächsten Tag das EN, gefolgt von einem ST. Da die Datensätze sofort per GPRS versendet werden, muß ich einige Abfragen durchführen. ST alleine wird nicht ausgegeben, sondern erst, wenn der zugehörige EN-Satz kommt.
Dieser gesamte TEil steuert die verschiedenen Möglichkeiten:
//Prüfung, ob temporäre Variablen gefüllt sind: Datum, Personalnummer und Typ. Wenn ja, wird Schleife abgebrochen
      if (($zstp==$laststp) && ($zper==$lastper) && ($ztyp==$lasttyp))
         {
         continue;
         }
// Wenn TempVAR Typ leer ist: Ausgabe nein, Füllen der TempVAR ja.
      if (empty($ztypsav))
         {
         $liste="0";  
         $kopieren="1";  
         }
//Wenn TempVAR Typ gefüllt ist, wird ausgegeben und weiter geprüft.
      else
         {
         $liste="1";  
//Erste Prüfung auf Typ: es steht ST drin
         if ($ztypsav=="ST")  
            {
//Zweite Prüfung auf Typ: es steht EN drin
            if ($ztyp=="EN")  
               {
//Wenn EN drin steht, wird dir TemVAR Zeit bis mit der ermittelten Uhrzeit gefüllt, aber nicht weg geschrieben
               $ztibsav=$ztim;
               $strtimbsav=$zstp;
               $kopieren="0";  
               }
//Wenn ST drin steht, wird die TempVAR gefüllt
            else
               {
               $kopieren="1";  
               }
            }
//Wenn TempVAR Typ mit EN gefüllt ist, und kein ST vorliegt,wird die von Zeit mit 00:00:00 gefüllt und weg geschrieben
         else
            {
            $ztivsav="00:00:00";  
            $kopieren="1";  
            }
         }
//Wenn Liste schreiben aktiviert ist, wird die ausgabedat aufgerufen und ausgeführt
      if ($liste=="1")  
         {
         $i++;
         include"ausgabedat.php";  
         }
//Wenn kopieren auf Null steht, werden die TempVars gelöscht
         if ($kopieren=="0")  
            {
            $ztypsav="";  
            $zdatsav="";  
            $zstpsav="";  
            $ztivsav="";  
            $ztibsav="";  
            $zpersav="";  
            $zprosav="";  
            $nnamesav="";  
            $vnamesav="";  
            $zstpstrv="";  
            $zstpstrb="";  
            }
//Ansonsten werden sie gefüllt
         else
            {
            $ztypsav=$ztyp;
            $zdatsav=$zdat;
            $zstpsav=$zstp;
            $zpersav=$zper;
            $zprosav=$zpro;
            $nnamesav=$nname;
            $vnamesav=$vname;


//Wenn der Typ ST lautet, wird die von-Zeit mit der ermittelten Zeit gefüllt, die bis-Zeit wird überschrieben
            if ($ztyp=="ST")  
               {
               $ztivsav=$ztim;
               $ztibsav="";  
               $strtimvsav=$zstp;
               }
//Bei EN wird die von-Zeit gelöscht uns die bis-Zeit gefüllt
            else
               {
               $ztivsav="";  
               $ztibsav=$ztim;
               $strtimbsav=$zstp;
               }
            }
//Speichern des letzten Datensatzes
         $lasttyp=$ztyp;
         $lastper=$zper;
         $laststp=$zstp;
      }

Bei der Entwicklung der Logik hat mir unser Entwickler unter die Arme gegriffen. Er kennt sich mit diesen Logiken aus, kennt aber php nicht.



Ich ziehe es vor, die Daten und Zeiten gleich bei der Abfrage mit
Hilfe von MySQL-Funktionen

zu ermitteln. Die MA-ID, das Datum und die Ein/Ausstempelzeit habe
ich durch zwei

Abfragen/Schleifen in einem Array als Arrays gespeichert.


Hier denke ich, daß alles in einer SQL Abfrage erledigt werden kann, da ich die ST und EN Info in der Datenbank bereits habe.


Ich vermute mal, du setzt eine Abfrage ab, und willst durch entspr.
if/else/then-Kontrollen prüfen, wann

der Wechsel von einer ID zur nächsten ID erfolgt um dann die
Zwischensumme auszugeben. Erstens glaube ich nicht,

das das geht, und wenn, dann nur mit erheblichem Aufwand. Da ist es
erheblich einfacher, zwei Schleifen zu programmieren,

so wie es Snowman schon geschrieben hat, bzw. wie es in meinem
Beispiel steht:

Genau das war mein erster Ansatz. Ist auch nicht weiter schwer (prinzipiell), funktioniert aber trotzdem nicht. Komme ich beim letzten MA an, habe ich keinen MA-Wechsel mehr. Die Schleife hört auf ohne Ausgabe einer Zwischensumme. Wenn hier der letzte Groschen fällt, löse ich es auf diese Weise. Ansonsten gehe ich auf die 2-Schleifen Variante.

Bei dieser SQL Abfrage habe ich immer Probleme gehabt. Wenn ich das date_format weg gelassen habe, hatte ich ein Ergebnis.

$query='select u_id,date_format(zeit,"%d.%m.%Y") as
Datum,min(time_to_sec(zeit)) as Beginn'
.' from zeiten2 where
date_format(zeit,"%Y-%m-%d") between
"'.$vonTag.'" and
"'.$bisTag.'"'
.' group by u_id,Datum';
$result=mysql_query($query);
/*
Mag daran liegen, daß die Original Datums-Daten in der Form "Jahr-Monat-Tag Stunde:Minute:Sekunde" vorliegen. Aber wie gesagt, da werde ich versuchen, mit einer einzigen SQL Abfrage alles erledigen zu können. Hier hilft die time_to_sec Funktion weiter. Die kannte ich noch nicht. Das wird sicher viel vereinfachen.

Bevor ich weitermachen kann, werden wieder einige Tage vergehen. Ausseneinsätze face-sad

Schöne Grüße
Christof

EDIT:
weitere Informationen zum besseren Verständnis:
Ich verwende zwei Tabellen. Die MA werden in einer Member Tabelle gepflegt.
Dort habe ich:
eine eindeutige ID, Nachname, Vorname, RFID Transponder Nummer, Projektzugehörigkeit

Die Daten werden in einer anderen Tabelle abgelegt. Sie sieht so aus:
Eindeutige ID, Typ ST oder EN (ztyp), Gerätenummer in der die Daten erfasst werden(zter), Erfassungszeit "Jahr-Monat-Tag Stunde:Minute:Sekunde" (zdat), Transpondernummer (zper), Projektnummer auf der gearbeitet wird (zpro) für weiter Verwendungen, das Datum wann der Datensatz gespeichert wurde (Timestamp)(datum), und noch ein Flag, um später markieren zu können, ob der Datensatz weiter verarbeitet worden ist (Abrechnung oder manuelle Änderung).

Bei Sortierung nach zper und zdat habe ich die zusammengehörenden Datensätze untereinander stehen, wenn sie komplett sind. Sind sie es nicht, fange ich in php die Sonderfälle ab.

Die Verknüpfung erfolgt über das Fels rfid in der Membertabelle und dem Feld zper in der Datentabelle.
Member: Guenni
Guenni Sep 27, 2009 at 23:01:11 (UTC)
Goto Top
Hi Christof,


Knackpunkt deiner Anwendung scheint ja die Zwischensumme zu sein. Ich versuch jetzt mal

zu erklären, warum das mit zwei Schleifen einfacher, wenn nicht sogar logischer zu

lösen ist.


Ich frage jetzt die Zeiten zu einer ID ab und summiere diese:

<?php

 $query="select . . . from tabelle2 where u_id=1";  

 . . . 

 while($row=mysql_fetch_array( . . . usw.)){

   echo "Ausgabe der abgefragten Felder";  

   $zwisum+=$row['std']; /* Wobei die Stunden bei dir berechnet werden. */  

 }
 
 
 /* Nach Beenden der Schleife wird die Zwischensumme ausgegeben. */
  
   echo $zwisum;

?>

Innerhalb der while-Schleife können noch deine Berechnungen etc. erfolgen.


Um jetzt die gleichen Werte für jede ID abzufragen/zu berechnen, müßte ich

den Ausdruck "where u_id=1" jedesmal ändern bzw. die ID über ein Formular eingeben.


Da liegt es doch nahe, den Ausdruck "dynamisch" zu erstellen, sprich, einen Algorithmus

zu entwickeln, der mir "solange wie vorhanden" ID's liefert:

<?php

$query1="select id, . . . from tabelle1 join tabelle2 on tabelle1.id=tabelle2.u_id";  
?>

Eine while-Schleife liefert mit die ID, und weitere Details, der MA, die in der Personaltabelle ist

sowie in der Zeitentabelle, sprich, wer gestempelt hat.


Innerhalb dieser äußeren Schleife packe ich nun den oben prog. Code und ersetzte den

Ausdruck "where u_id=1" durch "where u_id=".$row1['id'].


Vereinfacht heißt das:

Solange die äußere Schleife eine ID liefert, wird die innere Schleife ausgeführt, macht Berechnungen,

gibt Details zur ID aus etc.. Wenn keine ID mehr kommt, wird die innere Schleife auch nicht mehr ausgeführt.


Zum Problem date_format( . . .

Auch in meiner Tabelle liegt die Ein-/Ausstempelzeit im Format "Jahr-Monat-Tag Stunde:Minute:Sekunde" vor.

Die Funktion ist in der Lage, den Datumsanteil in deutscher Schreibweise auszugeben:

select date_format(feldname,"%d.%m.%Y") as Datum , . . . usw.

Ich kann aber den Alias Datum nicht in der where-Klausel verwenden,

auch wenn ich schreiben würde:

select date_format(feldname,"%Y-%m-%d") as Datum, weil Datum kein gültiger Spaltenname ist.

Da muß ich tatsächlich schreiben ". . . where date_format(spaltenname,"%Y-%m-%d) . . . "


Zur Tabelle der MA:

Das Feld "Projektzugehörigkeit" gehört in die Tabelle, in der die Zeiten erfasst werden.

Denn nur so erfahre ich, wieviele Std ein MA anwelchem Projekt gearbeitet hat.


Gruß
Günni
Member: gechger
gechger Oct 27, 2009 at 14:48:45 (UTC)
Goto Top
Hallo Günni,

habe jetzt wieder an meinem Zwischensummenproblem weitermachen können und es tatsächlich auch gelöst. Ich habe keine Zwei Schleifen Funktion benutzt, sondern den Userwechsel direkt abgefragt.

 
//Wenn temporäre Variable mit Personalnummer leer ist, wird diese durch aktuelle Personalnummer gefüllt.
        if (empty($zpertemp))
           {
           $zpertemp=$zper;
           }

        //Wenn aktuelle Personalnummer mit PersNummer des letzten Durchlaufs identisch ist, prüfe..
        if ($zpertemp==$zper)
             {

             //..ob die Unixzeit größer Null ist, Daten also komplett sind. Dann soll die ZwSumme hoch addiert werden...
             if ($unixzeit > 0)
               {
               $zwsummead+=$unixzeit;

               }

          }

//echo "zpertemp=$zpertemp","<br>"; 
//echo "zper=$zper","<br>"; 
//echo "zwsummead=$zwsummead","<br>"; 

        if ($zpertemp != $zper )
              {

               $zwsummeun=$zwsummead;
               $algstunden=round(($zwsummeun/3600),2);
               $algminuten=round((fmod($zwsummeun, 3600)/60),2);
               $algsekunden=round((fmod($algminuten, 60)/60),2);
               $algstwert=explode(".",$algstunden);  
               $algminwert=explode(".",$algminuten);  
               $zwsumme=$algstwert.":".sprintf("%02d",$algminwert);  

               $adzwsum+=$zwsummeun;
               if ($unixzeit > 0)
                  {

                  $zwsummead=$unixzeit;
                  }
               else
                  {
                  $zwsummead=0;
                  }
               $zpertemp=$zper;
               //$endsumme=$gsumme - $adzwsumme;
               echo "Zwischensumme=$zwsumme","<br>";  

               include"ausgabedat.php";  

               $zwsummeun="";  
               $zwsumme="";  

             $c="1";  

            }

Dies ist der Kern der Zwischensummenberechnung.

Problem:
Beim letzten User wird keine Zwischensumme ausgegeben.
Lösung:
ich addiere alle Zwischensummen, die ausgegeben worden sind. Ebenso addiere ich alle Einzelzeiten.
Die Summe aller Einzelzeiten ist identisch mit meiner Gesamtzeit. Davon ziehe ich die bisher ausgegebenen Zwischensummen ab. Die Differenz entspricht der Zwischensumme des letzten Users, die ich am Ende ausgeben kann.

   $lastsum=$gsummead - $adzwsum;
   $alglaststd=round(($lastsum/3600),2);
   $alglastmin=round((fmod($lastsum, 3600)/60),2);
   $algwertstd=explode(".",$alglaststd);  
   $algwertmin=explode(".",$alglastmin);  
   $endsumme=$algwertstd.":".sprintf("%02d",$algwertmin);  

echo "Zwischensumme= $endsumme","<br><br>";  

   $gsumstd=round(($gsummead/3600),2);
   $gsummin=round((fmod($gsummead, 3600)/60),2);
   $gwertstd=explode(".",$gsumstd);  
   $gwertmin=explode(".",$gsummin);  
   $gsumme=$gwertstd.":".sprintf("%02d",$gwertmin);  

echo "Gesamtstunden= $gsumme","<br><br>";  

Hört sich im ersten Moment kompliziert an, ist aber letztendlich gar nicht so schwierig

Jedenfalls vielen Dank an Alle für Eure Ideen

Schöne Grüße
Christof