【问题标题】:How to make SANDIWCH leave query in MySql?如何在 MySql 中进行 SANDWICH 离开查询?
【发布时间】:2020-02-29 06:54:10
【问题描述】:

你们中的许多人可能都知道三明治休假。但是如何在数据库级别实现对我来说有点困难。我有一个场景,如果员工休假 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

我想如果 remark 像 LEAVE 、 WEEK-OFF 和 LEAVE 一样,那么 WEEK-OFF 也应该转换为 LEAVE。

插入什么并不重要,但是在使用 SELECT 查询时,如果它发现 YESTERDAY 为 LEAVE,TODAY 为 WEEK-OFF,然后在第二天再次为 LEAVE,那么 WEEK-OFF 日也应该被视为 LEAVE。我希望我能清楚地说明我的问题,并且非常感谢任何帮助。

【问题讨论】:

  • 花时间修改公司的休假政策会更好。
  • @Strawberry,你的建议似乎不错,但我需要一个三明治休假的解决方案。另外,我正在尽我所能找到解决方案。如果您有解决方案,请分享。
  • @Strawberry,我知道我什至分享了我在最后一行产生的虚拟数据。我认为这足以理解我想要什么。从我正在挣扎的事情来看。我还分享了 6、7、8、10 的 Id 详细信息,大家比较容易理解。
  • @Strawberry,请建议您还需要做什么才能理解我的查询。我会相应地详细说明。
  • 很明显,你还没看懂

标签: mysql database-administration


【解决方案1】:

解决此问题的一种方法是转换所有非休息日的内容,以便我们可以为每个休息日分配一个数字(下面查询中的块)。之后,我们可以知道每个块中的最小和最大日期,并且可以在主查询中使用一个简单的子查询来确定最小和最大日期之前和之后的备注是否为离开。 例如

+--------+-----------------+-------------+--------------+----------+
| 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 是我的数据库中的表名。

请注意,您的数据必须是干净的,并且假设每个员工每天都有一个条目。我在你的输出中包含了比你需要的更多的列,这样你就可以看到发生了什么。

【讨论】:

  • 嗨 @P.Salmon,此查询非常适合单身员工,但在 1 个月的数据提取中需要花费大量时间来处理 150 多名员工
  • 运行解释计划并考虑添加索引。
猜你喜欢
  • 2010-10-07
  • 2010-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多