计算两个记录并将它们连接在同一个MySQL行中

时间:2018-05-14 05:37:36

标签: mysql sql database

我有一张表,其中包含员工访问控制的数据,不同行中办公室的入口和出口。 输出基于日期和时间范围示例中的查询 ac_date >= '2018-05-12' AND ac_date <= '2018-05-13' AND ac_time >='08:00:00' AND ac_time <= '13:00:00']

表格AC

CREATE TABLE `AC` (
  `employee` int(11) NOT NULL,
  `ac_date` date NOT NULL,
  `ac_time` time NOT NULL,
  `ac_event` tinyint(4) NOT NULL,
  KEY `index2` (`employee`,`ac_date`,`ac_time`,`ac_event`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Employee     ac_date        ac_time     ac_event
2032        2018-05-12      08:52:00        1
2032        2018-05-12      11:39:33        0
2032        2018-05-12      11:48:06        1
2032        2018-05-12      11:52:54        0
2032        2018-05-12      11:59:54        1
2032        2018-05-12      12:23:40        0
2032        2018-05-13      08:34:43        1
2032        2018-05-13      09:02:25        0
2032        2018-05-13      09:12:16        1
2032        2018-05-13      11:45:21        0
2032        2018-05-13      12:50:40        1
2032        2018-05-13      12:52:16        0

其中ac_ event = 1是输入,ac event = 0是输出。

我需要创建一个查询,显示同一行(进入/退出)中的数据,然后花时间让员工在办公室外停留。 EJ:

第一个块的输出:

Employee    entry_date   exit_date      entry_date   exit_date  duration 
2032        2018-05-12   2018-05-12     08:52:00     11:39:33   02:47:33

我能够通过使用相同的表执行查询来获得结果,但是我有重复的数据,因此我必须通过[ac_date,ac_time]求助于该组。我不知道我正在尝试的方式是否正确,所以我希望看到专家的解决方案。谢谢!

更新 http://sqlfiddle.com/#!9/6f36f/3

5 个答案:

答案 0 :(得分:2)

这是原始查询的简化版本:

select AC.employee, AC.ac_date, AC.ac_time,
   min(AC2.ac_time) as exit_time,
   timediff(min(AC2.ac_time), AC.ac_time)
from AC
left join AC as AC2
  on AC2.ac_date = AC.ac_date 
 and AC2.ac_time > ac.ac_time
 and AC2.ac_event = 0
where AC.ac_event = 1
  AND AC.ac_date >= '2018-05-11'
  AND AC.ac_date <= '2018-05-13'
  AND AC.ac_time >= '00:00:00'
  AND AC.ac_time <= '23:59:00'
group by AC.employee, AC.ac_date, AC.ac_time
order by AC.employee, AC.ac_date, AC.ac_time
;

请参阅fiddle

但无论你怎么写它,它总是一种非等连接(即不基于=),如果没有匹配的索引,性能可能会很差。

顺便说一句,如果这是MariaDB或MySQL 8,那么LAG / LEAD将是一项简单的任务。

答案 1 :(得分:1)

试试这个:

SELECT 
    *, 
    TIMEDIFF(exit_time,entry_time) as duration
FROM
    (
        SELECT 
            Employee, 
            ac_date as entry_date,
            ac_date as exit_date,
            MIN(CASE WHEN ac_event=1 THEN ac_time END) entry_time,
            MAX(CASE WHEN ac_event=0 THEN ac_time END) exit_time
        FROM
            AC
        GROUP BY 
            Employee,
            ac_date
    ) as t

答案 2 :(得分:1)

您可以使用相关子查询来获取进入/退出对:

SELECT employee, ac_date AS entry_date,
       (SELECT ac_date 
       FROM AC AS i
       WHERE i.ac_event = 0 
             AND i.employee = t.employee 
             AND i.ac_date >= t.ac_date 
             AND i.ac_time >= t.ac_time
       ORDER BY i.ac_date, i.ac_time LIMIT 1) AS exit_date,
       ac_time AS entry_time,       
       (SELECT ac_time
       FROM AC AS i
       WHERE i.ac_event = 0 
             AND i.employee = t.employee 
             AND i.ac_date >= t.ac_date 
             AND i.ac_time >= t.ac_time
       ORDER BY i.ac_date, i.ac_time LIMIT 1) AS exit_time       
FROM AC AS t
WHERE t.ac_date BETWEEN '2018-05-12' AND '2018-05-13' AND t.ac_event = 1;

<强>输出:

employee entry_date  exit_date   entry_time  exit_time
-------------------------------------------------------
2032     2018-05-12  2018-05-12  08:52:00    11:39:33
2032     2018-05-12  2018-05-12  11:48:06    11:52:54
2032     2018-05-12  2018-05-12  11:59:54    12:23:40
2032     2018-05-13  2018-05-13  08:34:43    09:02:25
2032     2018-05-13  2018-05-13  09:12:16    11:45:21
2032     2018-05-13  2018-05-13  12:50:40    12:52:16

然后您可以使用TIMESTAMPDIFF来计算每个条目之间的时间 - 退出:

SELECT employee, entry_date, exit_date, entry_time, exit_time, 
        SEC_TO_TIME(TIMESTAMPDIFF(SECOND,
                                  CONCAT(entry_date, ' ', entry_time), 
                                  CONCAT(exit_date, ' ', exit_time))) AS duration
FROM (
SELECT employee, ac_date AS entry_date,
       (SELECT ac_date 
       FROM AC AS i
       WHERE i.ac_event = 0 
             AND i.employee = t.employee 
             AND i.ac_date >= t.ac_date 
             AND i.ac_time >= t.ac_time
       ORDER BY i.ac_date, i.ac_time LIMIT 1) AS exit_date,
       ac_time AS entry_time,       
       (SELECT ac_time
       FROM AC AS i
       WHERE i.ac_event = 0 
             AND i.employee = t.employee 
             AND i.ac_date >= t.ac_date 
             AND i.ac_time >= t.ac_time
       ORDER BY i.ac_date, i.ac_time LIMIT 1) AS exit_time       
FROM AC AS t
WHERE t.ac_date BETWEEN '2018-05-12' AND '2018-05-13' AND t.ac_event = 1) AS x

Demo here

答案 3 :(得分:0)

尝试这个:只需对每个条目employee, ac_date, ac_time进行排序,然后在单独的集合中退出然后加入,这是一种可以使用{{1如果您希望记录同时包含入口和出口

,则代替INNER JOIN以获得更好的性能
LEFT JOIN

输出 http://sqlfiddle.com/#!9/891d91/18

答案 4 :(得分:0)

另一个选择是对同一个表使用双连接以获取退出记录:

SELECT t1.employee, 
       t1.ac_date AS entry_date,
       t1.ac_time AS entry_time,
       t2.ac_date AS exit_date,
       t2.ac_time AS exit_time,
       SEC_TO_TIME(TIMESTAMPDIFF(SECOND,
                                  CONCAT(t1.ac_date, ' ', t1.ac_time), 
                                  CONCAT(t2.ac_date, ' ', t2.ac_time))) AS duration       

/* fetches entry records */
FROM AC AS t1       

/* fetches exit records that occur after the correlated entry record */
INNER JOIN AC AS t2 

   ON t1.employee = t2.employee AND t2.ac_event = 0
      AND CONCAT(t1.ac_date, ' ', t1.ac_time) <= CONCAT(t2.ac_date, ' ', t2.ac_time) 

/* fetches exit records that occur between t1, t2 records */
LEFT JOIN AC AS t3  
   ON t2.employee = t3.employee AND t3.ac_event = 0 
      AND CONCAT(t3.ac_date, ' ', t3.ac_time) >= CONCAT(t1.ac_date, ' ', t1.ac_time) 
      AND CONCAT(t3.ac_date, ' ', t3.ac_time) < CONCAT(t2.ac_date, ' ', t2.ac_time) 
WHERE t1.ac_date BETWEEN '2018-05-12' AND '2018-05-13' 
      AND t1.ac_event = 1 
      AND t3.employee IS NULL /* There is no record between t1 entry and t2 exit */

Demo here