繁体   English   中英

MySQL - PHP:计算一天中多个事件之间的总小时数

[英]MySQL - PHP: Calculate total hours in a day between multiple events

我有一张名为time_track 的表:

+----+--------+---------------------+---------+
| 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       |
+----+--------+---------------------+---------+

其中eventid 1 = Start workeventid 2 = Stop work

考虑到工作时间是所有 eventid 的 1 和 2 之间的总小时数,我如何计算任何给定日期的小时数 - WHERE emplid = 13 AND Year(ctimestamp) = 2016 and Month(ctimestamp) = 06 and Day(ctimestamp) = 02

您也可以使用 PHP(而不是 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.
?>

您可以将结果四舍五入。

将以前的代码复制粘贴到文件中,将其另存为 .PHP 并在浏览器中打开它。 随意更改示例数据。

编辑:调用函数来计算所有小时数更容易:

<?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.
?>

使用从查询中获得的 sql-result 作为参数调用此函数(并将foreach替换为while ( $row = mysqli_fetch_array )。

与下面仅使用 SQL 的答案不同,将 SQL 和 PHP 结合起来以提高可读性和性能似乎很有效。 此外,PHP 将允许您编写代码来处理诸如丢失或重复数据之类的异常。

<?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
?>

运行代码: http : //phpfiddle.org/main/code/jx8h-ztuy

下面是上述代码的经过测试的改进,包括数据库访问和循环。
由于您指出性能至关重要,因此您可能需要使用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";
}
?>

您必须在子目录中自行加入您的表,并获得大于 eventid=1 的 eventid=2 的最小值,并计算这 2 条记录之间的差异。 在外部查询中,您总结了 eventid=1 时间戳的天数差异:

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)

在实时环境中,我不会将解决方案基于没有间隙的 id 列,即使它是自动增量列。 通常有间隙。 您还需要决定如果您有孤立签入或签出事件会发生什么。 我的代码假设每次签入都有相应的签出。

你可以试试这样的

如果您需要进一步分离您的数据,您也可以按年和月分组。

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)

您需要计算事件 1 和事件 2 的时间戳之间的差异。这确实假设对于任何给定的一天,每个 empl_id 只有 2 个事件并且结帐时间在同一天(例如,隔夜时间将未显示在此查询中)。 这不是一个非常强大的解决方案,但我不确定您的数据的完整性以及您需要处理的边缘情况。

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

SELECT UNIX_TIMESTAMP('2010-11-29 13:16:55') - UNIX_TIMESTAMP('2010-11-29 13:13:55') 作为输出

$entry_time = [... 使用查询在此处获取...]

$exit_time = [...使用查询在此处获取...]

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

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

$time_entry = [...] // 查找查询并将输出保存到此变量

$time exit = [...] // 查找查询并保存输出到这个变量

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM