如何在 MySql 中进行 SANDWICH 假查询?
How to make SANDIWCH leave query in MySql?
你们中的许多人可能都知道三明治假。但是如何在数据库级别实现对我来说有点困难。我有一个场景,如果员工休假 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_FIDDLElinktable created in fiddler
我想如果评论像 LEAVE 、 WEEK-OFF 和 LEAVE 一样,那么 WEEK-OFF 也应该转换为 LEAVE 。
插入什么并不重要,但是在使用 SELECT 查询时,如果它发现 YESTERDAY 为 LEAVE,TODAY 为 WEEK-OFF,然后第二天再次为 LEAVE,那么应该考虑 WEEK-OFF 日和离开一样。我希望我的查询清楚,我们将不胜感激任何帮助。
解决此问题的一种方法是转换非休假的所有内容,以便我们可以为每个休假块分配一个数字(下面查询中的 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 是我数据库中的 table 名称。
注意您的数据必须是干净的,并且假设每天每个员工都有一个条目。我在输出中包含了比您需要的更多的列,以便您可以看到发生了什么。
你们中的许多人可能都知道三明治假。但是如何在数据库级别实现对我来说有点困难。我有一个场景,如果员工休假 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_FIDDLElinktable created in fiddler
我想如果评论像 LEAVE 、 WEEK-OFF 和 LEAVE 一样,那么 WEEK-OFF 也应该转换为 LEAVE 。
插入什么并不重要,但是在使用 SELECT 查询时,如果它发现 YESTERDAY 为 LEAVE,TODAY 为 WEEK-OFF,然后第二天再次为 LEAVE,那么应该考虑 WEEK-OFF 日和离开一样。我希望我的查询清楚,我们将不胜感激任何帮助。
解决此问题的一种方法是转换非休假的所有内容,以便我们可以为每个休假块分配一个数字(下面查询中的 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 是我数据库中的 table 名称。
注意您的数据必须是干净的,并且假设每天每个员工都有一个条目。我在输出中包含了比您需要的更多的列,以便您可以看到发生了什么。