【问题标题】:Calculate time differences during MySQL query计算 MySQL 查询期间的时间差
【发布时间】:2014-12-17 10:07:04
【问题描述】:

我有两个 MySQL 表,都包含 DATETIME 字段:

  1. beginnings 包含启动某个进程的时间
  2. events 包含由该进程引起某些事件的时间

开始是在晚上;事件发生在第二天早上。请注意,每个开始之后都会有数量不断变化的多个事件!

这里有两个示例表:

CREATE TABLE IF NOT EXISTS `beginnings` (
  `bid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `begin` datetime NOT NULL,
  PRIMARY KEY (`bid`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;

INSERT INTO `beginnings` (`bid`, `begin`) VALUES
(1, '2014-09-24 22:44:00'),
(2, '2014-09-25 21:32:00'),
(3, '2014-09-28 22:01:00');

CREATE TABLE IF NOT EXISTS `events` (
  `eid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `event` datetime NOT NULL,
  PRIMARY KEY (`eid`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=10 ;

INSERT INTO `events` (`eid`, `event`) VALUES
(1, '2014-09-25 02:07:00'),
(2, '2014-09-25 04:06:00'),
(3, '2014-09-25 05:50:00'),
(4, '2014-09-26 02:07:00'),
(5, '2014-09-26 04:15:00'),
(7, '2014-09-29 01:08:00'),
(8, '2014-09-29 04:21:00'),
(6, '2014-09-29 05:02:00'),
(9, '2014-09-29 05:25:00');

我想计算每个事件和各自开始之间的时间差(以分钟为单位)。

目前我正在从数据库中提取数据,将日期时间条目转换为时间戳,并计算 PHP 中的差异。这是一个简化的示例代码,其中包含数字,便于阅读:

$events = range(1, 50);
$beginnings = array(7, 11, 19, 27, 29, 36, 40, 43);
$events = array_merge(array_diff($events, $beginnings));

$differences = array();
for($i = 0; $i < count($beginnings); $i++) {
    foreach($events as $event) {
        if($event > $beginnings[$i] && $event < $beginnings[$i + 1]) {
            $differences[] = $event - $beginnings[$i];
        }
    }
}

我的问题:

是否可以在查询时在 MySQL 中计算出这些差异?


示例:

你是一家汽车厂的老板。每天晚上,在你睡觉之前,你告诉夜班的团队每人制造一辆汽车,并记录他们何时完成。当你早上回来时,你想计算不同团队建造各自的汽车花了多长时间。您会看到一个团队在两个小时内完成了他们的汽车,而另一个团队需要五个小时来完成他们的汽车。你很好奇,所以你会记下一年的时间,然后做一些统计。

【问题讨论】:

  • events 表与beginnings 表有什么关系?
  • @AbhikChakraborty 我不确定我是否理解您的问题。每个事件(清晨的某个时间)都从一个开始(前一个晚上的某个时间)开始。每个开始都可能引发多个事件,因此多个事件共享一个开始。希望这是有道理的。
  • 为什么开始是一个单独的表?当然,“开始”只是一系列事件中的第一个事件?
  • @Strawberry 是的,开始和事件都可以列在同一个表中,并有一列标识哪些日期时间是哪些。如果这样的数据库设计有助于实现我的目标,请说明如何实现。

标签: php mysql datetime


【解决方案1】:

好吧,如果是我,我可能会做这样的事情......

DROP TABLE project_events;

CREATE TABLE project_events
(project_id INT NOT NULL
,event_date DATETIME NOT NULL
);

INSERT INTO project_events VALUES
(1, '2014-09-24 22:44:00'),
(1, '2014-09-25 02:07:00'),
(1, '2014-09-25 04:06:00'),
(1, '2014-09-25 05:50:00'),

(2, '2014-09-25 21:32:00'),
(2, '2014-09-26 02:07:00'),
(2, '2014-09-26 04:15:00'),

(3, '2014-09-28 22:01:00'),
(3, '2014-09-29 01:08:00'),
(3, '2014-09-29 04:21:00'),
(3, '2014-09-29 05:02:00'),
(3, '2014-09-29 05:25:00');

SELECT project_id
     , MIN(event_date) start
     , MAX(event_date) end
     , TIMEDIFF(MAX(event_date)
     , MIN(event_date)) duration 
  FROM project_events 
 GROUP 
    BY project_id;
+------------+---------------------+---------------------+----------+
| project_id | start               | end                 | duration |
+------------+---------------------+---------------------+----------+
|          1 | 2014-09-24 22:44:00 | 2014-09-25 05:50:00 | 07:06:00 |
|          2 | 2014-09-25 21:32:00 | 2014-09-26 04:15:00 | 06:43:00 |
|          3 | 2014-09-28 22:01:00 | 2014-09-29 05:25:00 | 07:24:00 |
+------------+---------------------+---------------------+----------+

此架构允许项目过度运行或重叠的可能性。您可能有充分的理由希望将“开始”存储在单独的表中 - 但调整此解决方案以考虑到这一点将是微不足道的。它只涉及到 project_id 的连接。

编辑:这是将开始日期与该项目的所有后续日期进行比较的一种方法:

SELECT x.project_id
     , MIN(y.event_date) start
     , x.event_date end
     , TIMEDIFF(x.event_date,MIN(y.event_date)) duration 
  FROM project_events x 
  JOIN project_events y 
    ON y.project_id = x.project_id 
 GROUP 
    BY x.project_id,x.event_date;
+------------+---------------------+---------------------+----------+
| project_id | start               | end                 | duration |
+------------+---------------------+---------------------+----------+
|          1 | 2014-09-24 22:44:00 | 2014-09-24 22:44:00 | 00:00:00 |
|          1 | 2014-09-24 22:44:00 | 2014-09-25 02:07:00 | 03:23:00 |
|          1 | 2014-09-24 22:44:00 | 2014-09-25 04:06:00 | 05:22:00 |
|          1 | 2014-09-24 22:44:00 | 2014-09-25 05:50:00 | 07:06:00 |
|          2 | 2014-09-25 21:32:00 | 2014-09-25 21:32:00 | 00:00:00 |
|          2 | 2014-09-25 21:32:00 | 2014-09-26 02:07:00 | 04:35:00 |
|          2 | 2014-09-25 21:32:00 | 2014-09-26 04:15:00 | 06:43:00 |
|          3 | 2014-09-28 22:01:00 | 2014-09-28 22:01:00 | 00:00:00 |
|          3 | 2014-09-28 22:01:00 | 2014-09-29 01:08:00 | 03:07:00 |
|          3 | 2014-09-28 22:01:00 | 2014-09-29 04:21:00 | 06:20:00 |
|          3 | 2014-09-28 22:01:00 | 2014-09-29 05:02:00 | 07:01:00 |
|          3 | 2014-09-28 22:01:00 | 2014-09-29 05:25:00 | 07:24:00 |
+------------+---------------------+---------------------+----------+

【讨论】:

    【解决方案2】:

    您可以使用函数datedifftimediff

    SELECT DATEDIFF('2010-11-30 23:59:59','2010-12-31');
        -> -31
    

    http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_datediff

    SELECT TIMEDIFF('2008-12-31 23:59:59.000001', '2008-12-30 01:01:01.000002');
        -> '46:58:57.999999'
    

    http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_timediff


    您还可以将两个 DateTime 值转换为整数(自 Unix 纪元以来的秒数)并像普通数字一样减去它们。
    SELECT UNIX_TIMESTAMP('2007-11-30 10:30:19');
        -> 1196440219
    

    http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_unix-timestamp


    响应以下 cmets 的示例。但是,您应该阅读 SELECT、JOIN 和 WHERE 语句。这是简单的 MySQL,在使用更复杂的函数之前应该理解。 见http://dev.mysql.com/doc/refman/5.7/en/select.html

    SELECT TIMEDIFF(events.event, beginnings.begin)
    FROM event
    INNER JOIN beginnings ON beginnings.eid = events.eid
    

    注意根据您的表结构,此选择将​​不起作用,因为 begins 表缺少与事件的关系 ID。我在这里使用了eid。在使用该选择之前,您需要了解这两个表是如何相互关联的,并相应地进行调整。不能在这里为您提供帮助,因为我可以看到您的桌子与 atm 的关系。您的旧代码在 PHP 中硬编码了关系,这意味着您不能在 MySQL 中执行此选择。

    【讨论】:

    • 我明白了。我不明白的是如何让 MySQL 知道我想要从多次表中计算特定时间的差异。
    • 您当然可以在函数中使用SELECT column FROM table 语句中的列名,而不是示例中的固定字符串。
    • 你的意思是SELECT DATEDIFF(event FROM events WHERE event &gt; begin AND event &lt; DATE(DATE_ADD(begin, INTERVAL +1 DAY)), begin FROM beginning);?
    • 没有。只需使用 JOIN 执行简单的 SELECT。我会尝试一些并将其添加到答案中
    • 我明白了。极好的。谢谢你。我会试试的。
    猜你喜欢
    • 1970-01-01
    • 2012-08-19
    • 2019-04-24
    • 1970-01-01
    • 2012-06-10
    • 1970-01-01
    • 2013-10-14
    • 2021-05-27
    • 2012-01-23
    相关资源
    最近更新 更多