Если предположить, что никто не работает в течение полуночи, вы можете агрегировать данные по компании, сотруднику и дате, используя профсоюз, чтобы захватить любого, кто вышел, но не вошел в систему. Например,
drop table if exists t;
create table t
(company int, employee int, dt datetime , state varchar(5));
insert into t values
(1,1,'2020-03-01 08:00:01','cin'),
(1,1,'2020-03-01 08:00:02','cin'),
(1,1,'2020-03-01 08:00:03','cout'),
(1,1,'2020-03-02 08:00:01','cin'),
(1,1,'2020-03-02 08:00:02','cout'),
(1,1,'2020-03-02 08:00:03','cout'),
(1,1,'2020-03-03 08:00:01','cin'),
(1,1,'2020-03-04 08:00:01','cout')
;
select company,employee,'cin', min(dt) cin,'cout',
(select max(dt)
from t t1
where t1.company = t.company and
t1.employee = t.employee and
date(t1.dt) = date(t.dt) and
state = 'cout') cout
from t
where state = 'cin'
group by company,employee,date(dt)
union all
select company,employee,'cin', null cin,
'cout', max(dt)
from t
where state = 'cout' and
(select min(dt)
from t t1
where t1.company = t.company and
t1.employee = t.employee and
date(t1.dt) = date(t.dt) and
state = 'cin') is null
group by company,employee,date(dt);
+---------+----------+-----+---------------------+------+---------------------+
| company | employee | cin | cin | cout | cout |
+---------+----------+-----+---------------------+------+---------------------+
| 1 | 1 | cin | 2020-03-01 08:00:01 | cout | 2020-03-01 08:00:03 |
| 1 | 1 | cin | 2020-03-02 08:00:01 | cout | 2020-03-02 08:00:03 |
| 1 | 1 | cin | 2020-03-03 08:00:01 | cout | NULL |
| 1 | 1 | cin | NULL | cout | 2020-03-04 08:00:01 |
+---------+----------+-----+---------------------+------+---------------------+
4 rows in set (0.002 sec)