2016-06-30 28 views
9

Ho questa tabella denominata time_track:MySQL - PHP: Calcola ore totali in un giorno tra più eventi

+----+--------+---------------------+---------+ 
| id | emplid | ctimestamp   | eventid | 
+----+--------+---------------------+---------+ 
| 1 | 13  | 2016-06-02 03:41:41 | 1  | 
+----+--------+---------------------+---------+ 
| 2 | 13  | 2016-06-02 09:04:49 | 2  | 
+----+--------+---------------------+---------+ 
| 3 | 13  | 2016-06-02 10:03:13 | 1  | 
+----+--------+---------------------+---------+ 
| 4 | 13  | 2016-06-02 13:21:23 | 2  | 
+----+--------+---------------------+---------+ 

dove eventid 1 = Start work e eventid 2 = Stop work.

Come posso calcolare le ore di un dato giorno tenendo conto che le ore di lavoro sono le ore totali tra tutti eventid di 1 e 2-WHERE emplid = 13 AND Year(ctimestamp) = 2016 and Month(ctimestamp) = 06 and Day(ctimestamp) = 02

+1

Cosa intendi per "ore totali in un giorno"? Vuoi dire solo ore totali tra i due timestamp? – Devon

+0

Penso che sia un timesheet - quindi Fred entra alle 03:41 e lavora fino alle 09:04 ... fa un pisolino per un'ora e torna di nuovo alle 10:03 fino alle 13:21 - quindi è quell'ammontare cumulativo. Tuttavia, se * * * viene interrotto di giorno, le 21:00 -> 04:00 causeranno problemi poiché il giorno specificato avrà un evento * stop work * ma nessun evento * start work * precedente. – CD001

+1

Si tratta di una query che verrà eseguita frequentemente sul database in modo che le prestazioni delle query siano fondamentali? In tal caso, sei disposto a prendere in considerazione uno schema alternativo? Come pensate di affrontare le condizioni marginali come quelle indicate sopra da @ CD001 oi casi in cui vi sono tempi di fine/inizio non appaiati (cioè dati incompleti)? –

risposta

4

Si può anche farlo con PHP (invece di SQL):

<?php 
$data = array(array("1","2016-06-02 03:41:41"), 
       array("2","2016-06-02 09:04:49"), 
       array("1","2016-06-02 10:03:13"), 
       array("2","2016-06-02 13:21:23") 
      ); 
$hours = 0; 
foreach ($data as $row) // PROCESS ALL ROWS FROM QUERY. 
{ if ($row[ 0 ] == "1") // IF CURRENT ROW IS START TIME 
     $start = strtotime($row[ 1 ]); 
    else { $stop = strtotime($row[ 1 ]); // STOP TIME. CALCULATE. 
     $hours += ($stop - $start)/3600; 
     } 
} 
echo $hours; // 8.6883333333333. 
?> 

È possibile arrotondare il risultato.

Copia-incolla il codice precedente in un file, salvalo come .PHP e aprilo nel tuo browser. Sentiti libero di cambiare i dati del campione.

Edit: è più facile chiamare una funzione per calcolare tutte le ore:

<?php 

function total_hours ($data) 
{ $hours = 0; 
    foreach ($data as $row) 
    if ($row[ "eventid" ] == "1") 
     $start = strtotime($row[ "ctimestamp" ]); 
    else { $stop = strtotime($row[ "ctimestamp" ]); 
      $hours += ($stop - $start)/3600; 
     } 
    return $hours; 
} 

$sample_data = array(array("id"   => 1, 
          "emplid"  => 13, 
          "ctimestamp" => "2016-06-02 03:41:41", 
          "eventid" => 1), 
         array("id"   => 2, 
          "emplid"  => 13, 
          "ctimestamp" => "2016-06-02 09:04:49", 
          "eventid" => 2), 
         array("id"   => 3, 
          "emplid"  => 13, 
          "ctimestamp" => "2016-06-02 10:03:13", 
          "eventid" => 1), 
         array("id"   => 4, 
          "emplid"  => 13, 
          "ctimestamp" => "2016-06-02 13:21:23", 
          "eventid" => 2) 
        ); 
echo total_hours($sample_data); // 8.6883333333333. 
?> 

chiamata questa funzione con l'sql-risultato che si ottiene dalla query come parametri (e sostituire la foreach da while ($row = mysqli_fetch_array).

1

Si può provare qualcosa di simile

È possibile raggruppare da anno e mese anche se è necessario separare ulteriormente i dati.

select day(ctimestamp) as Day, hour(ctimestamp) as Hour, count(*) as Count 
from MyTable 
where ctimestamp between :date1 and :date2 
group by day(ctimestamp), hour(ctimestamp) 
+0

Dovresti fornire qualche spiegazione al tuo approccio. –

-1

DOMANDA: SELECT DATEDIFF('2010-10-08 18:23:13', '2010-09-21 21:40:36') AS days;

$ time_entry = [...] // trova di query e salvare fuori messo a questa

$ uscita tempo variabile = [...] // trova query e salva put in questa variabile

$ query = "SELECT DATEDIFF('". time_entry "', '" .time_exit "') AS days;"

+0

Riferimento: http://stackoverflow.com/a/8070343/6419332 –

+0

Dovresti fornire alcune spiegazioni al tuo approccio –

+0

sebbene siano valori php, sostituiscono il nome della variabile con il contenuto (Qui il contenuto è il timestamp), memorizzandolo in variabile $ query AS ** String **. quindi il valore come $ query = SELECT UNIX_TIMESTAMP (2010-11-29 13:16:55) - UNIX_TIMESTAMP (2010-11-29 13:13:55) come giorni; sarà memorizzato in variabile e questo verrà passato a sql! [@MikeBrant] –

0

SELEZIONA UNIX_TIMESTAMP ('2010-11-29 13:16:55') -.. UNIX_TIMESTAMP ('2010-11-29 13:13:55') come uscita

$entry_time = [... ottenerlo qui utilizzando interrogazione ...]

$exit_time = [... ottenerlo qui utilizzando interrogazione ...]

$ query = "SELECT UNIX_TIMESTAMP(' ". $ Entry_time." ') - UNIX_TIMESTAMP(' ". $exit_time. "') as days;";

+0

Perché si dovrebbe usare SQL per sottrarre due variabili PHP? – PaulH

+0

sebbene siano valori php, essi ** sostituiscono ** * nome variabile * con * contenuto * (Qui * contenuto * è * timestamp *), memorizzandolo nella variabile * $ query * AS ** String **. quindi il valore come $ query = SELEZIONA UNIX_TIMESTAMP (2010-11-29 13:16:55) - UNIX_TIMESTAMP (2010-11-29 13:13:55) come giorni; verrà memorizzato in variabile e questo verrà passato a sql! @PaulH –

+0

Sì, ma perché usare SQL?Perché non scrivere solo PHP: '$ query = $ exit_time - $ entry_time'. Usare SQL qui è come prendere una macchina per camminare a 2 metri o iarde. – PaulH

3

Diversamente dalle risposte SQL di seguito, è efficiente combinare SQL e PHP per la leggibilità e le prestazioni. Inoltre, PHP ti permetterà di codificare come gestire eccezioni come dati mancanti o doppi.

<?php 
// Generate test data like it would be returned by a PDO::fetchAll(PDO:::FETCH_ASSOC) with 
// SELECT * FROM time_track 
// WHERE emplid = 13 AND Year(ctimestamp) = 2016 and Month(ctimestamp) = 06 and Day(ctimestamp) = 02 
// ORDER BY ctimestamp 
$logs[] = ['ctimestamp'=>'2016-06-02 03:41:41', 'eventid'=>1]; 
$logs[] = ['ctimestamp'=>'2016-06-02 09:04:49', 'eventid'=>2]; 
$logs[] = ['ctimestamp'=>'2016-06-02 10:03:13', 'eventid'=>1]; 
$logs[] = ['ctimestamp'=>'2016-06-02 13:21:23', 'eventid'=>2]; 

// Compute working time 
$worktime = 0; // in seconds 
foreach ($logs as $log) { 
    if ($log['eventid'] == 1) { // start work 
     $startWork = $log['ctimestamp']; 
    } else { // end work 
     $endWork = $log['ctimestamp']; 
     $worktime += strtotime($endWork) - strtotime($startWork); 
    } 
} 
echo gmdate('H:i', $worktime); // seconds to hours:minutes 
?> 

codice in esecuzione: http://phpfiddle.org/main/code/jx8h-ztuy

Di seguito è riportato un perfezionamento testata del codice di cui sopra compreso accesso al database e un ciclo.
Dal momento che si indicano le prestazioni è cruciale, si consiglia di utilizzare PDO::prepare()

<pre> 
<?php 
class TimeLog { 
    private $pdo; 
    private $pdoStatement; 

    // constructor: connect to database and prepare statement 
    function __construct(){ 
     // adapt following constructor to your database configuartion 
     $this->pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8mb4', 'username', 'password'); 
     $this->pdoStatement = $this->pdo->prepare(
      'SELECT * FROM time_track 
       WHERE emplid = :emplid 
       AND DATE(ctimestamp) = :cdatestamp 
       ORDER BY ctimestamp 
     '); 
    } 

    // compute workTime for given employee and date 
    function workTime($emplid, $cdatestamp) { 
     // fetch from database, executing prepared statement 
     $this->pdoStatement->execute([':emplid'=>$emplid, ':cdatestamp'=>$cdatestamp]); 
     $logs = $this->pdoStatement->fetchAll(PDO::FETCH_ASSOC); 

     // compute working time 
     $worktime = 0; // in seconds 
     foreach ($logs as $log) { 
      if ($log['eventid'] == 1) { // start work 
      $startWork = $log['ctimestamp']; 
      } else { // end work 
       $endWork = $log['ctimestamp']; 
       $worktime += strtotime($endWork) - strtotime($startWork); 
      } 
     } 
     return gmdate('H:i', $worktime); // convert seconds to hours:minutes 
    } 
} 

$timeLog = new Timelog(); // invoke constructor once 

$emplid = 13; // example 

// echo registration of last seven days 
for ($date = strtotime('-7 days'); $date <= time(); $date += 24 * 3600) { 
    // convert $date to YYYY-MM-DD format 
    $cdatestamp = date('Y-m-d', $date); 
    // execute one SQL statement and return computed worktime 
    $workTime = $timeLog->workTime($emplid, $cdatestamp); 
    // show result 
    echo $cdatestamp, "\t", $workTime, "\n"; 
} 
?> 
+1

È necessario fornire alcune spiegazioni insieme all'esempio di codice. –

+0

@ Mike Brant, fatto – PaulH

2

Bisogna self-join vostra tabella su se stessa in un subuery e ottenere il min di EventID = 2 che è maggiore del EventID = 1 e calcola la differenza tra questi 2 record. Nella query esterna si somma delle differenze di giorno dei EventID = 1 timestamp:

select date(t3.ctimestamp), t3.emplid, sum(diff)/60 as hours_worked 
from 
    (select t1.id, t1.ctimestamp, t1.emplid, min(t2.ctimestamp) as mintime, timestampdiff(minute,min(t2.ctimestamp), t1.ctimestamp) as diff 
    from yourtable t1 
    inner join yourtable t2 on t1.ctimestamp<t2.ctimestamp 
    where t1.eventid=1 
      and t2.eventid=2 
      and t1.emplid=13 
      and t2.emplid=13 
      and date(t1.ctimestamp)='2016-06-02' --may have checked out the next day, I do not know how you want to handle that 
    group by t1.id, t1.ctimestamp, t1.emplid) t3 
group by date(t3.ctimestamp) 

In un ambiente vivo non mi baso una soluzione sul colonna id non avere lacune, anche se si tratta di un incremento automatico colonna. Di solito ci sono lacune. È inoltre necessario decidere cosa succede se si dispone di check-in orfani o di check-out eventi. Il mio codice presuppone che ogni check in abbia un check out corrispondente.

1

Sarà necessario calcolare la differenza tra i timestamp per gli eventi di 1 e gli eventi di 2. Ciò presuppone che per un dato giorno, ci sono solo 2 eventi per empl_id e il tempo di checkout è all'interno dello stesso giorno (ad esempio , le ore notturne non verranno visualizzate in questa query). Non è una soluzione molto solida, ma non sono sicuro dell'integrità dei dati e dei casi limite che è necessario gestire.

SELECT TIMEDIFF(t1.c_timestamp, t2.c_timestamp)/3600 AS hourDiff 
FROM time_track t1 
INNER JOIN time_track t2 
ON (t2.id > t1.id AND t2.event_id = 2 AND t1.empl_id = t2.empl_id AND DAY(t2.c_timestamp) = DAY(t1.c_timestamp) AND MONTH(t2.c_timestamp) = MONTH(t1.c_timestamp) AND YEAR(t2.c_timestamp) = YEAR(t1.c_timestamp)) 
WHERE t1.event_id = 1 AND t1.empl_id = 13 AND Year(t1.c_timestamp) = 2016 and Month(t1.c_timestamp) = 6 and Day(t1.c_timestamp) = 2