MySQL 查询以捕获事件之间的工作时间

MySQL query to capture the working time between events

我正在尝试创建一个查询来捕获事件发生所需的工作时间。在下面的数据中,我想显示帐户从 ACTIVATED 变为 DEACTIVATED 所需的工作时间。

ENCODEDKEY  TRANSACTIONID   LOANPRODUCTKEY  TIMESTAMP              TYPE
1           2067            aa1             2015/02/06 15:29:00    LOAN_PRODUCT_ACTIVATED
2           2162            aa1             2015/02/16 14:07:00    LOAN_PRODUCT_EDITED
3           2666            aa1             2015/02/16 15:29:00    LOAN_PRODUCT_DEACTIVATED
4           3456            aa2             2015/03/06 12:01:00    LOAN_PRODUCT_ACTIVATED
5           3478            aa2             2015/03/08 13:15:00    LOAN_PRODUCT_EDITED
6           3908            aa2             2015/03/18 13:15:00    LOAN_PRODUCT_DEACTIVATED

所以结果会是这样的

LOANPRODUCTKEY          TIME
aa1                     24:00:00
aa2                     12:00:00

(我知道这些数字是错误的!)

我也需要它只考虑工作时间(即早上 9 点到下午 5 点)这可能吗?

感谢任何能帮助我的人。

更新。 非常感谢到目前为止提供帮助的人!

所以我设法创建了一个查询,该查询将 return 每个 loanproductkey 的激活和停用打字机的正确日期。但是,我仍在努力计算两个计算日期之间的工作时间。我的查询如下:

SELECT

att.LOANPRODUCTKEY
,sub1.time_activated
,sub2.time_deactivated

from 
activity att

left join (select
min(att.TIMESTAMP) as time_activated
,att.loanproductkey
from
activity att
where
att.`TYPE` = "LOAN_PRODUCT_ACTIVATED"
group by
att.LOANPRODUCTKEY) AS sub1
ON att.LOANPRODUCTKEY = sub1.LOANPRODUCTKEY

left join 
(select
max(att.timestamp) as time_deactivated
,att.LOANPRODUCTKEY
from
activity att
where
att.`TYPE` = "LOAN_PRODUCT_DEACTIVATED"
group by
att.LOANPRODUCTKEY) AS sub2
ON att.LOANPRODUCTKEY = sub2.LOANPRODUCTKEY

group by
att.loanproductkey

好的,所以我认为下面的查询应该可以工作,尽管它可能可以进行很多调整。我猜也很有可能做得更聪明:)

逻辑如下:

  1. 首先,它使用数字 table 为每个 loanproductkey 生成开始日期和结束日期之间的日期序列(这是在派生的 table 中完成的)。

  2. 然后它加入这个table并计算开始和结束之间的整个工作日数(不包括)并将这个(乘以240得到分钟数)添加到分钟数这是开始天数时间和 17:00 之间的差异,以及 09:00 和结束天数时间之间的分钟差异。

所以计算是这样的:

(minutes from start time of first day to 17:00) -- eg. '17:00:00'-'15:29:00'
+
(minutes from 09:00 to end time of last day) -- eg. '15:29:00'-'09:00'
+
(number of working days between start and end) * 240

您可能需要稍微调整数学,但逻辑应该是合理的。

做出的假设:

  • 周末是 weekday 函数返回的工作日 5 和 6 - 这可能取决于本地服务器设置,我不确定。

  • 存在一个名为 numbers 的 table,其列 num 包含数字 1 到 至少需要覆盖您的范围从开始到结束的最大日期范围。如果你没有这个,我已经在最后描述了你如何创建它。

这会得到如下结果:

LOANPRODUCTKEY  total_min   total_time (hhh:mm:ss)
aa1             4800        80:00:00
aa2             5834        97:14:00

Sample SQL Fiddle(没有 total_time 列,因为 fiddle 使用 java,它不喜欢小时部分的大值。

SELECT 
    t_start.LOANPRODUCTKEY

    , (
        TIMESTAMPDIFF(MINUTE, CAST(t_start.timestamp AS time), CAST('17:00:00' AS time))
      + TIMESTAMPDIFF(MINUTE, CAST('09:00:00' AS time), CAST(t_end.timestamp AS time))
      + COUNT(WEEKDAY(t_start.timestamp) NOT IN (5,6)) * 8 * 60
    ) AS total_minutes

    , SEC_TO_TIME(
        TIMESTAMPDIFF(SECOND, CAST(t_start.timestamp AS time), CAST('17:00:00' AS time)) 
      + TIMESTAMPDIFF(SECOND, CAST('09:00:00' AS time), CAST(t_end.timestamp AS time)) 
      + COUNT(WEEKDAY(t_start.timestamp) NOT IN (5,6)) * 8 * 60 * 60
    ) AS total_time

FROM 
    t t_start
JOIN 
    t t_end ON t_start.LOANPRODUCTKEY = t_end.LOANPRODUCTKEY
JOIN 
(
    SELECT 
        ts.LOANPRODUCTKEY
        , DATE(DATE_ADD(ts.timestamp,INTERVAL num DAY)) AS datesSeries
    FROM 
        t ts
    JOIN 
        t te ON ts.LOANPRODUCTKEY = te.LOANPRODUCTKEY
    CROSS JOIN
        numbers r
    WHERE 
        num < DATEDIFF(te.timestamp, ts.timestamp) 
     AND 
        ts.TYPE = 'LOAN_PRODUCT_ACTIVATED'
     AND 
        te.TYPE = 'LOAN_PRODUCT_DEACTIVATED'
) dates ON t_start.LOANPRODUCTKEY = dates.LOANPRODUCTKEY

WHERE 
    t_start.TYPE = 'LOAN_PRODUCT_ACTIVATED' AND t_end.TYPE = 'LOAN_PRODUCT_DEACTIVATED'
GROUP BY 
    t_start.LOANPRODUCTKEY, t_start.TIMESTAMP, t_end.TIMESTAMP
ORDER BY 
    t_start.LOANPRODUCTKEY;

如果您还没有 suitable table 的数字序列涵盖开始日期和结束日期之间的最大天数,您可以创建一个 table使用下面的查询填充数字 1-1000,我从 this answer.

中获取
CREATE TABLE numbers (num int primary key);

INSERT INTO numbers
SELECT SEQ.SeqValue
FROM (
    SELECT (HUNDREDS.SeqValue + TENS.SeqValue + ONES.SeqValue) SeqValue
    FROM (
        SELECT 0 SeqValue UNION ALL SELECT 1 SeqValue UNION ALL
        SELECT 2 SeqValue UNION ALL SELECT 3 SeqValue UNION ALL
        SELECT 4 SeqValue UNION ALL SELECT 5 SeqValue UNION ALL
        SELECT 6 SeqValue UNION ALL SELECT 7 SeqValue UNION ALL
        SELECT 8 SeqValue UNION ALL SELECT 9 SeqValue
    ) ONES
    CROSS JOIN (
        SELECT  0 SeqValue UNION ALL SELECT 10 SeqValue UNION ALL
        SELECT 20 SeqValue UNION ALL SELECT 30 SeqValue UNION ALL
        SELECT 40 SeqValue UNION ALL SELECT 50 SeqValue UNION ALL
        SELECT 60 SeqValue UNION ALL SELECT 70 SeqValue UNION ALL
        SELECT 80 SeqValue UNION ALL SELECT 90 SeqValue
    ) TENS
    CROSS JOIN (
        SELECT   0 SeqValue UNION ALL SELECT 100 SeqValue UNION ALL
        SELECT 200 SeqValue UNION ALL SELECT 300 SeqValue UNION ALL
        SELECT 400 SeqValue UNION ALL SELECT 500 SeqValue UNION ALL
        SELECT 600 SeqValue UNION ALL SELECT 700 SeqValue UNION ALL
        SELECT 800 SeqValue UNION ALL SELECT 900 SeqValue
    ) HUNDREDS
) SEQ WHERE SEQ.SeqValue > 0;