Mysql История перерыва сотрудников с разницей во времени - PullRequest
3 голосов
/ 21 сентября 2019

У меня есть данные биометрического журнала сотрудника с флагом inoutmode.Я пытаюсь получить подробный список времени перерыва и с разницей во времени.

inoutmode 4 в качестве прорыва и 5 в качестве взлома.

INSERT INTO `tbl_downloadentry` (`EmpMachineID`, `shift_date`, `AttenTime`, `InOutMode`) VALUES (105, '2019-09-19', '14:00:13', 4);
INSERT INTO `tbl_downloadentry` (`EmpMachineID`, `shift_date`, `AttenTime`, `InOutMode`) VALUES (105, '2019-09-19', '16:07:08', 4);
INSERT INTO `tbl_downloadentry` (`EmpMachineID`, `shift_date`, `AttenTime`, `InOutMode`) VALUES (105, '2019-09-19', '16:07:18', 5);
INSERT INTO `tbl_downloadentry` (`EmpMachineID`, `shift_date`, `AttenTime`, `InOutMode`) VALUES (235, '2019-09-19', '15:44:26', 4);
INSERT INTO `tbl_downloadentry` (`EmpMachineID`, `shift_date`, `AttenTime`, `InOutMode`) VALUES (235, '2019-09-19', '16:37:58', 4);
INSERT INTO `tbl_downloadentry` (`EmpMachineID`, `shift_date`, `AttenTime`, `InOutMode`) VALUES (235, '2019-09-19', '20:01:11', 5);
INSERT INTO `tbl_downloadentry` (`EmpMachineID`, `shift_date`, `AttenTime`, `InOutMode`) VALUES (235, '2019-09-19', '20:01:25', 5);
INSERT INTO `tbl_downloadentry` (`EmpMachineID`, `shift_date`, `AttenTime`, `InOutMode`) VALUES (235, '2019-09-19', '20:30:29', 4);
INSERT INTO `tbl_downloadentry` (`EmpMachineID`, `shift_date`, `AttenTime`, `InOutMode`) VALUES (326, '2019-09-19', '15:58:30', 4);
INSERT INTO `tbl_downloadentry` (`EmpMachineID`, `shift_date`, `AttenTime`, `InOutMode`) VALUES (326, '2019-09-19', '19:34:09', 5);
INSERT INTO `tbl_downloadentry` (`EmpMachineID`, `shift_date`, `AttenTime`, `InOutMode`) VALUES (327, '2019-09-19', '15:44:19', 5);
INSERT INTO `tbl_downloadentry` (`EmpMachineID`, `shift_date`, `AttenTime`, `InOutMode`) VALUES (327, '2019-09-19', '15:55:37', 4);
INSERT INTO `tbl_downloadentry` (`EmpMachineID`, `shift_date`, `AttenTime`, `InOutMode`) VALUES (327, '2019-09-19', '19:59:38', 4);

вот мой ЖелаемыйВывод

| EmpMachineID | attendance_date | break_out  | break_in  | Diff      |
|--------------|-----------------|------------|-----------|-----------|
| 235          | 2019-09-19      | 15:44:26   |           |           |
|              | 2019-09-19      | 16:37:58   |           |           |
|              | 2019-09-19      |            | 20:01:11  |           |
|              | 2019-09-19      | 20:30:29   | 20:01:25  |    29:04  |
| 326          | 2019-09-19      | 19:34:09   | 15:58:30  | 03:35:39  |

Я старался изо всех сил, чтобы добиться результата.Вот мой попытанный запрос:

SELECT l2.empmachineid,
       l2.shift_date,
       l2.attentime,
       l2.inoutmode
FROM   tbl_downloadentry AS l2
WHERE  l2.inoutmode IN ( 5, 4 )
       AND l2.shift_date = "2019-09-19"
ORDER  BY l2.empmachineid,
          l2.shift_date,
          l2.attentime ASC  

Моя версия MySQL = 10.3.17-MariaDB-1-log

SELECT l2.EmpMachineID, l2.shift_date, l2.InOutMode, 
       case when l2.InOutMode=5 then l2.AttenTime END AS BreakOut, 
       case when l2.InOutMode=4 then l2.AttenTime END AS BreakIn
FROM tbl_downloadentry AS l2
WHERE l2.InOutMode IN (5, 4) AND l2.shift_date="2019-09-19"
ORDER BY l2.EmpMachineID, l2.shift_date, l2.AttenTime ASC

частично я получил записи, используя региструсловие, но 4 и 5 inoutmode должны быть в одной строке, чтобы рассчитать разницу во времени.Любая идея ...

Любые предложения приветствуются.

Ответы [ 2 ]

4 голосов
/ 21 сентября 2019

Вот подход, использующий LEAD() и LAG() Оконные функции, доступные в MariaDB 10.2+ и MySQL 8 +.

  • Когдастрока строки InOutMode в режиме 4 означает, что это время break_out.Теперь мы используем функцию LAG(), чтобы получить немедленную предыдущую строку для этого конкретного EmpID.Порядок определяется на основе времени.Таким образом, если режим InOutMode непосредственно предыдущей строки равен 5, это означает, что у нас есть соответствующее break_in время для этого break_out времени, иначе null.
  • Аналогичный процесс применяется длястрока с режимом InOutMode, равным 5.Единственная разница на этот раз в том, что вместо этого мы используем LEAD() функцию;потому что нам нужно получить непосредственную следующую строку и проверить, является ли она break_out или нет.
  • Теперь нам просто нужно использовать этот набор результатов как ПроизводноеТаблица и DISTINCT выходят (потому что у нас будут повторяющиеся строки для каждого случая, когда есть break_in и break_out вместе).Кроме того, во внешнем запросе мы можем вычислить разницу во времени, используя функциональность TimeDiff().

Следующий запрос выполняется для EmpID = 235 для демонстрационной цели:

SELECT 
  DISTINCT 
    dt.*, 
    TIMEDIFF(dt.break_out, dt.break_in) AS diff 
FROM 
(
SELECT  
  EmpMachineID, 
  shift_date, 
  CASE InOutMode 
    WHEN 4 THEN AttenTime -- this is break_out row
    WHEN 5 THEN -- this is break_in row, find the break_out if exists
      CASE 
        WHEN LEAD(InOutMode) OVER w = 4 
        THEN LEAD(AttenTime) OVER w 
      END       
  END AS break_out, 
  CASE InOutMode 
    WHEN 5 THEN AttenTime -- this is break_in row
    WHEN 4 THEN   -- this is break_out row, find the break_in if exists
      CASE 
        WHEN LAG(InOutMode) OVER w = 5 
        THEN LAG(AttenTime) OVER w 
      END  
  END AS break_in
FROM tbl_downloadentry 
WHERE EmpMachineID = 235 
AND InOutMode IN (4,5) 
AND shift_date = '2019-09-19' 
WINDOW w AS (PARTITION BY EmpMachineID
             ORDER BY AttenTime ASC)
) AS dt;

Результат

| EmpMachineID | shift_date | break_out | break_in | diff     |
| ------------ | ---------- | --------- | -------- | -------- |
| 235          | 2019-09-19 | 15:44:26  |          |          |
| 235          | 2019-09-19 | 16:37:58  |          |          |
| 235          | 2019-09-19 |           | 20:01:11 |          |
| 235          | 2019-09-19 | 20:30:29  | 20:01:25 | 00:29:04 |

Просмотр на БД Fiddle

0 голосов
/ 21 сентября 2019

Если я правильно понимаю проблему, вы хотите связать «4» записи с «5» записями.Ваш результирующий набор, похоже, не имеет ничего общего с вашими выборочными данными, поэтому им трудно следовать.

При следующем подходе каждое 4/5 связывается с ближайшим.Это достигается назначением группировки, подсчетом для каждого «4» и понижением для каждого «5».

Однако это не совсем полное решение, поскольку может быть несколько взлетов и падений в виде серии«4» и «5» подряд.Таким образом, он назначает вторичную группировку, чтобы разбить ее на части.

SELECT EmpMachineID, shift_date,
       MAX(CASE WHEN InOutMode = 4 THEN AttenTime END),
       MAX(CASE WHEN InOutMode = 5 THEN AttenTime END)
FROM (SELECT dl.*,
             SUM(InOutMode = group_first_InOutMode) OVER (PARTITION BY dl.EmpMachineID, dl.shift_date, dl.grouping ORDER BY dl.AttenTime) as secondary_grouping
      FROM (SELECT dl.*,
                   FIRST_VALUE(InOutMode) OVER (PARTITION BY dl.EmpMachineID, dl.shift_date, dl.grouping ORDER BY dl.AttenTime) as group_first_InOutMode
            FROM (SELECT dl.*,
                         SUM( CASE WHEN InOutMode = 4 THEN 1 WHEN InOutMode = 5 THEN -1 END) OVER
                              (PARTITION BY dl.EmpMachineID, dl.shift_date
                               ORDER BY dl.AttenTime
                              ) -
                              (CASE WHEN InOutMode = 5 THEN -1 ELSE 0 END) as grouping  -- subtract out "5"s on current row
                 FROM tbl_downloadentry dl
                 WHERE dl.InOutMode IN (5, 4) AND dl.shift_date = '2019-09-19'
                ) dl
           ) dl
     ) dl
GROUP BY EmpMachineID, shift_date, grouping, secondary_grouping;

Здесь - это db <> скрипка.

...