你们中许多人可能知道三明治休假。但是对我来说,如何在数据库级别实现它有点困难。我有一个场景,如果员工在休假之前和之后休了2个假,那么这3天都应标记为``假''。以下是我要分享的基本要求。
HERE IS REQUIREMENT OF SANDWICH LEAVE
以下是一些示例数据。我可能最终会想到用相应的期望结果来补充它。
CREATE TABLE IF NOT EXISTS `atnsystem` (
`Emp_id` int unsigned NULL,
`attendance_date` date NULL,
`in_datetime` datetime NULL,
`out_datetime` datetime NULL,
`remark` varchar(100) NULL
) DEFAULT CHARSET=utf8;
INSERT INTO `atnsystem` (`Emp_id`, `attendance_date`, `remark`) VALUES
('66', '2020-02-17', 'LEAVE'),
('66', '2020-02-16', 'WEEK-OFF'),
('66', '2020-02-15', 'LEAVE');
这是SQL_FIDDLE链接table created in fiddler
我想如果备注再次像LEAVE,WEEK-OFF和LEAVE,那么WEEK-OFF也应该转换为LEAVE。
插入的内容无关紧要,但是在使用SELECT查询时,如果它发现YESTERDAY为LEAVE,而TODAY为WEEK-OFF,然后第二天又为LEAVE,则WEEK-OFF天也应被视为LEAVE。希望我能清楚地说明我的问题,我们将不胜感激。
一种解决方法是转换所有非休假时间,以便我们可以为每个休假时间段分配一个数字(在下面的查询中为bloc)。之后,我们可以知道每个块中的最小和最大日期,并可以在主查询中使用一个简单的子查询来确定是否在紧接最小和最大日期之前和之后的备注。例如
+--------+-----------------+-------------+--------------+----------+
| Emp_id | attendance_date | in_datetime | out_datetime | remark |
+--------+-----------------+-------------+--------------+----------+
| 66 | 2020-02-29 | NULL | NULL | week-off |
| 66 | 2020-02-28 | NULL | NULL | NULL |
| 66 | 2020-02-27 | NULL | NULL | leave |
| 66 | 2020-02-26 | NULL | NULL | week-off |
| 66 | 2020-02-25 | NULL | NULL | week-off |
| 66 | 2020-02-24 | NULL | NULL | NULL |
| 66 | 2020-02-23 | NULL | NULL | leave |
| 66 | 2020-02-22 | NULL | NULL | week-off |
| 66 | 2020-02-21 | NULL | NULL | week-off |
| 66 | 2020-02-20 | NULL | NULL | leave |
| 66 | 2020-02-19 | NULL | NULL | NULL |
| 66 | 2020-02-18 | NULL | NULL | leave |
| 66 | 2020-02-17 | NULL | NULL | NULL |
| 66 | 2020-02-16 | NULL | NULL | WEEK-OFF |
| 66 | 2020-02-15 | NULL | NULL | LEAVE |
| 66 | 2020-02-14 | NULL | NULL | leave |
+--------+-----------------+-------------+--------------+----------+
16 rows in set (0.00 sec)
select t.*, b.*,
(select t1.remark
from t t1
where t1.emp_id = t.emp_id and
t1.attendance_date < b.mindt
order by t1.attendance_date desc limit 1) previous_remark,
(select t1.remark
from t t1
where t1.emp_id = t.emp_id and
t1.attendance_date > b.maxdt
order by t1.attendance_date asc limit 1) next_remark ,
case
when
(select t1.remark
from t t1
where t1.emp_id = t.emp_id and
t1.attendance_date < b.mindt
order by t1.attendance_date desc limit 1) = 'leave'
AND
(select t1.remark
from t t1
where t1.emp_id = t.emp_id and
t1.attendance_date > b.maxdt
order by t1.attendance_date asc limit 1) ='leave' THEN
'leave'
ELSE t.remark
END as final_remark
from t
left join
(
select a.emp_id,a.bloc,min(a.attendance_date) mindt,max(a.attendance_date) maxdt
from
(
select s.*,
if(newremark = 'p',0,If(newremark <> @p,@b:=@b+1,@b:=@b)) bloc,
@p:=newremark p
from
(
select t.*,
case when remark = 'week-off' then remark else 'p' end as newremark
from t
order by attendance_date asc
) s
cross join (select@b:=0,@p:='') b
order by attendance_date asc
) a
where bloc > 0
group by a.emp_id, a.bloc
) b
on b.emp_id = t.emp_id and t.attendance_date between b.mindt and b.maxdt
order by t.emp_id,t.attendance_date;
+--------+-----------------+-------------+--------------+----------+--------+------+------------+------------+-----------------+-------------+--------------+
| Emp_id | attendance_date | in_datetime | out_datetime | remark | emp_id | bloc | mindt | maxdt | previous_remark | next_remark | final_remark |
+--------+-----------------+-------------+--------------+----------+--------+------+------------+------------+-----------------+-------------+--------------+
| 66 | 2020-02-14 | NULL | NULL | leave | NULL | NULL | NULL | NULL | NULL | NULL | leave |
| 66 | 2020-02-15 | NULL | NULL | LEAVE | NULL | NULL | NULL | NULL | NULL | NULL | LEAVE |
| 66 | 2020-02-16 | NULL | NULL | WEEK-OFF | 66 | 1 | 2020-02-16 | 2020-02-16 | LEAVE | NULL | WEEK-OFF |
| 66 | 2020-02-17 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
| 66 | 2020-02-18 | NULL | NULL | leave | NULL | NULL | NULL | NULL | NULL | NULL | leave |
| 66 | 2020-02-19 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
| 66 | 2020-02-20 | NULL | NULL | leave | NULL | NULL | NULL | NULL | NULL | NULL | leave |
| 66 | 2020-02-21 | NULL | NULL | week-off | 66 | 2 | 2020-02-21 | 2020-02-22 | leave | leave | leave |
| 66 | 2020-02-22 | NULL | NULL | week-off | 66 | 2 | 2020-02-21 | 2020-02-22 | leave | leave | leave |
| 66 | 2020-02-23 | NULL | NULL | leave | NULL | NULL | NULL | NULL | NULL | NULL | leave |
| 66 | 2020-02-24 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
| 66 | 2020-02-25 | NULL | NULL | week-off | 66 | 3 | 2020-02-25 | 2020-02-26 | NULL | leave | week-off |
| 66 | 2020-02-26 | NULL | NULL | week-off | 66 | 3 | 2020-02-25 | 2020-02-26 | NULL | leave | week-off |
| 66 | 2020-02-27 | NULL | NULL | leave | NULL | NULL | NULL | NULL | NULL | NULL | leave |
| 66 | 2020-02-28 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
| 66 | 2020-02-29 | NULL | NULL | week-off | 66 | 4 | 2020-02-29 | 2020-02-29 | NULL | NULL | week-off |
+--------+-----------------+-------------+--------------+----------+--------+------+------------+------------+-----------------+-------------+--------------+
16 rows in set (0.16 sec)
t是数据库中的表名。
注意,您的数据必须是干净的,并且假设每个员工每天都有一个条目。我在输出中包含的列超出了您的要求,因此您可以查看正在发生的情况。