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 work
和 eventid 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 name 为 contents (这里是 content 是 timestamp),将其存储到变量 $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:计算一天中多个事件之间的总小时数的主要内容,如果未能解决你的问题,请参考以下文章
查找一天中事件的开始时间和结束时间 - Pandas 时间序列 - 这样结束时间不会落入第二天