有没有更好的方法来定义此查询中的时间范围?

时间:2019-07-11 08:41:04

标签: mysql

我们这里有一个名为attendance_log的表,该表捕获了登录和注销时间,我正在使用查询来准确获取和计算工作时间。我提出的查询实际上效果很好,但是我想知道是否有一种方法可以缩短某些部分。以下是我查询的基础:

不幸的是,我的雇主仍在使用MySQL 4.1,所以..

SELECT staffid,DATE(CHECKINOUT) AS dci,
       MIN(TIME(CHECKINOUT)) atin,
       CASE WHEN MIN(TIME(CHECKINOUT)) <= '08:30:59' THEN '08:30:00'
       WHEN MIN(TIME(CHECKINOUT)) BETWEEN '08:31:00' AND '09:00:59' THEN '09:00:00'
       WHEN MIN(TIME(CHECKINOUT)) BETWEEN '09:01:00' AND '09:30:59' THEN '09:30:00'
       WHEN MIN(TIME(CHECKINOUT)) BETWEEN '09:31:00' AND '10:00:59' THEN '10:00:00'
       WHEN MIN(TIME(CHECKINOUT)) BETWEEN '10:01:00' AND '10:30:59' THEN '10:30:00'
       WHEN MIN(TIME(CHECKINOUT)) BETWEEN '10:31:00' AND '11:00:59' THEN '11:00:00'
       WHEN MIN(TIME(CHECKINOUT)) BETWEEN '11:01:00' AND '11:30:59' THEN '11:30:00'
       WHEN MIN(TIME(CHECKINOUT)) BETWEEN '11:31:00' AND '12:00:59' THEN '12:00:00'
       WHEN MIN(TIME(CHECKINOUT)) BETWEEN '12:01:00' AND '12:30:59' THEN '12:30:00'
       WHEN MIN(TIME(CHECKINOUT)) BETWEEN '12:31:00' AND '13:00:59' THEN '13:00:00'
       END AS Time_IN,
       MAX(TIME(CHECKINOUT)) AS atout,
       CASE 
       WHEN MAX(TIME(CHECKINOUT)) <= '08:59:59' THEN '08:30:00' 
       WHEN MAX(TIME(CHECKINOUT)) <= '09:29:59' THEN '09:00:00' 
       WHEN MAX(TIME(CHECKINOUT)) <= '09:59:59' THEN '09:30:00' 
       WHEN MAX(TIME(CHECKINOUT)) <= '10:29:59' THEN '10:00:00' 
       WHEN MAX(TIME(CHECKINOUT)) <= '10:59:59' THEN '10:30:00' 
       WHEN MAX(TIME(CHECKINOUT)) <= '11:29:59' THEN '11:00:00' 
       WHEN MAX(TIME(CHECKINOUT)) <= '11:59:59' THEN '11:30:00' 
       WHEN MAX(TIME(CHECKINOUT)) <= '12:29:59' THEN '12:00:00'
       ELSE MAX(TIME(CHECKINOUT)) 
       END AS Time_OUT,
       DAYNAME(CHECKINOUT) AS dname,
       GROUP_CONCAT(TIME(checkinout) ORDER BY TIME(checkinout) SEPARATOR ' ') allci,COUNT(*) cnt
  FROM attendance_log, 
       (SELECT @nday:=28800, @hday:=16200, @rtime:=3600) v
 WHERE staffid='HS078' 
   AND MONTH(checkinout)=MONTH(CURDATE()-INTERVAL 1 MONTH) 
   AND YEAR(checkinout)=YEAR(CURDATE())
GROUP BY DATE(CHECKINOUT);

如您所见,CASE表达式中有太多操作无法同时获得Time_INTime_OUT值。我最近发现了更多有关在MySQL中使用变量的信息,但现在我将其用于其他功能,但不在此查询中。

我在这里准备了一个小提琴,查询的时间比我上面给出的示例长得多{= {3}}

所以我的问题是,如何缩短CASE表达式中的条件?也许我定义了第一个“有效时间”值,然后像这样每30分钟运行一次间隔?

编辑:

我设法通过“人为地”创建分钟和秒,同时增加1小时的登录时间并保留从记录中提取的注销时间来缩短查询时间;

SELECT staffid,
       DATE(CHECKINOUT) AS dci,
       DAYNAME(CHECKINOUT) AS dname,      
       MIN(TIME(CHECKINOUT)) atin,      
       CASE WHEN MIN(TIME(checkinout)) < '08:30:59' THEN '08:30:00' 
       WHEN MIN(TIME(checkinout)) > '08:30:59' THEN 
       CONCAT_WS(':',CASE WHEN MINUTE(MIN(TIME(checkinout))) > 30 THEN LPAD(HOUR(MIN(checkinout))+1,2,'0') 
       WHEN MINUTE(MIN(TIME(checkinout))) <= 30 THEN LPAD(HOUR(MIN(checkinout)),2,'0')END,
       CASE WHEN MINUTE(MIN(TIME(checkinout))) > 30 THEN '00' 
       WHEN MINUTE(MIN(TIME(checkinout))) <= 30 THEN 30 END,'00') END tin,       
       MAX(TIME(CHECKINOUT)) AS atout,      
       CASE WHEN COUNT(*)=4 AND MAX(TIME(checkinout)) < '18:00:00' THEN '17:30:00' 
       WHEN COUNT(*)=2 AND DAYNAME(checkinout)='Saturday' AND MAX(TIME(checkinout)) > '13:00:00' THEN '13:00:00'
       WHEN COUNT(*) IN (0,1,3) THEN 'Incomplete'
       ELSE CONCAT_WS(':',LPAD(HOUR(MAX(checkinout)),2,'0'),CASE WHEN MINUTE(MAX(TIME(checkinout))) < 30 THEN '00'
       WHEN MINUTE(MAX(TIME(checkinout))) >= 30 THEN 30 END,'00') END tout,
       GROUP_CONCAT(TIME(checkinout) ORDER BY TIME(checkinout) SEPARATOR ' ') allci,COUNT(*) cnt
  FROM attendance_log, (SELECT @nday:=28800, @hday:=16200, @rtime:=3600) v
 WHERE staffid='HS078' 
   AND MONTH(checkinout)=MONTH(CURDATE()) 
   AND YEAR(checkinout)=YEAR(CURDATE())
GROUP BY DATE(CHECKINOUT);

这个想法非常简单,无论员工是否在上午8:30之前登录,查询都将在上午8:30捕获,一旦超过该时间一分钟,将被视为延迟30分钟。但是,如果注销时间假设是下午05:30,则需要有30分钟的确切间隔才能被捕获为超时。例如:

  1. 于上午8:31登录,结果是上午9:00。
  2. 上午9:01登录导致了上午9:30
  3. 下午5:45退出是下午5:30
  4. 下午6:15退出是下午6:00。

额外:

  1. 每天分配1个小时的休息时间,该时间可以是从下午12:00到下午2:00的任何时间。例如,员工可以在下午12:15休息,但必须在下午1:15(或之前)回来。如果员工需要休息30分钟或更短的时间,则剩余的30分钟被视为替换当前日期之前发生的任何迟到的时间。
  2. 每两周,工作人员将有一个整天(上午8:30至下午5:30)轮班和半天(上午8:30至1:00 pm)轮班。

1 个答案:

答案 0 :(得分:0)

在使用@Barmar建议进行了一些测试之后,我发现他的方法比我尝试过的任何方法都要短:

SELECT A.*,
      CASE WHEN MIN(timechk) <= @chkin THEN @chkin 
      ELSE CEIL(MIN(timechk)/1800)*1800 END AS tin -- here I used @Barmar suggestion,
      MAX(timechk) atout,
      COUNT(*) AS cnt,
      GROUP_CONCAT(timechk ORDER BY timechk SEPARATOR ' ') timelist FROM
(SELECT staffid,DATE(CHECKINOUT) AS dci,checkinout,
       DAYNAME(CHECKINOUT) AS dname,
       TIME_TO_SEC(checkinout) AS timechk
  FROM att_log
 WHERE staffid='HF002' 
   AND MONTH(checkinout)=MONTH(CURDATE()-INTERVAL 1 MONTH) 
   AND YEAR(checkinout)=YEAR(CURDATE())) A, 
(SELECT @nday:=28800,@hday:=16200,@anhour:=3600,@chkin:=30600,@chkout:=63000,@min30:=1800) v
GROUP BY dci;

当然,我仍然需要手动执行许多操作,但是就基本查询而言,与之前的查询尝试相比,这满足了我的需求。