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

Posted

技术标签:

【中文标题】MySQL - PHP:计算一天中多个事件之间的总小时数【英文标题】:MySQL - PHP: Calculate total hours in a day between multiple events 【发布时间】:2016-11-02 19:44:16 【问题描述】:

我有一张名为 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

【问题讨论】:

total hours in a day 是什么意思?您是指两个时间戳之间的总小时数吗? 我认为这是一个时间表 - 所以 Fred 在 03:41 打卡上班,一直工作到 09:04 ... 小睡了一个小时,然后在 10:03 到 13:21 再次打卡 -所以这是累计金额。但是,如果它正在按天分解,则 21:00 -> 04:00 将导致问题,因为给定日期将有 stop work 事件但没有前面的 开始工作事件。 这是一个经常针对数据库运行的查询,因此查询性能至关重要?如果是这样,您愿意考虑替代模式吗?您打算如何解决上述@CD001 指出的边缘条件或存在不成对的结束/开始时间(即数据不完整)的情况? 刚刚编辑了我的答案:添加了一个函数来计算总小时数。 @Devon 我的意思是总工作时间,如果 1 是工作开始,2 是工作结束。事件 1 和 2 一天可以发生多次,所以我想要总工作时间 【参考方案1】:

选择 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;";

【讨论】:

为什么要使用 SQL 减去两个 php 变量? 虽然是 php 值,但它们 replace variable namecontents (这里是 contenttimestamp),将其存储到变量 $query AS ** String ** 中。然后像 $query = SELECT UNIX_TIMESTAMP(2010-11-29 13:16:55) - UNIX_TIMESTAMP(2010-11-29 13:13:55) 这样的值作为天数;将存储在变量中,这将传递给 sql! @PaulH 是的,但为什么要使用 SQL?为什么不只写 PHP:$query = $exit_time - $entry_time。在这里使用 SQL 就像坐车走 2 米或 2 码。 $查询是sql的语法!不是PHP!你可以试试看:link 如果你的 php 服务器(apache)是打开的! ` "SELECT UNIX_TIMESTAMP(2010-11-29 13:16:55) - UNIX_TIMESTAMP(2010-11-29 13:13:55)" ` 结果如下: ` |输出 | |180 |作为单个单元格表中的单元格:您可以尝试@PaulH 是的,我可以使用 SQL 进行减法,是的,$query 包含一个 SQL 字符串,但 $query = "SELECT 5 - 4"; 是一个 PHP 语句,它将一个包含 SQL 语句的字符串分配给一个 PHP 变量。【参考方案2】:

与下面的仅 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";

?>

【讨论】:

您应该提供一些解释以及代码示例。【参考方案3】:

您也可以使用 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 )。

【讨论】:

【参考方案4】:

您需要计算事件 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

【讨论】:

【参考方案5】:

您必须在子程序中自行加入您的表,并获得大于 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 列,即使它是一个自动增量列。通常会有间隙。如果您有孤儿签入或签出事件,您还需要决定会发生什么。我的代码假定每次签入都有相应的签出。

【讨论】:

【参考方案6】:

你可以试试这样的

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

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)

【讨论】:

你应该对你的方法提供一些解释。【参考方案7】:

查询: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;"

【讨论】:

你应该对你的方法提供一些解释 虽然它是 php 值,但它们将变量名称替换为内容(这里的内容是时间戳),并将其存储到变量 $query AS ** String ** 中。然后像 $query = SELECT UNIX_TIMESTAMP(2010-11-29 13:16:55) - UNIX_TIMESTAMP(2010-11-29 13:13:55) 这样的值作为天数;将存储在变量中,这将传递给 sql! [@MikeBrant]

以上是关于MySQL - PHP:计算一天中多个事件之间的总小时数的主要内容,如果未能解决你的问题,请参考以下文章

查找一天中花费的时间以及休息时间

具有多个计算的Mysql查询变慢

用于计算 emp 时间的 Min、Max 的 Pig 脚本

查找一天中事件的开始时间和结束时间 - Pandas 时间序列 - 这样结束时间不会落入第二天

提取关于0值的时间,求一天中的多个工作时间,在MongoDB数组操作中

使用 pandas 的滚动窗口计算一天中每个时间的平均值