MSSQL从TimeStamps之间的长度计算小时数

时间:2015-12-30 00:08:38

标签: sql sql-server sql-server-2008

我正在尝试从时钟数据库计算工时。我提出的查询存在一些问题,我无法弄清楚。

1)如果用户当天没有时钟,那么它认为用户工作直到时钟进入。如果用户忘记输入时钟,则应该抛弃查询

2)查询非常慢。有没有更好的方法可以用来加速它?

WITH AUXILIERY_TBL AS (
SELECT [First Name],Checktime,ROW_NUMBER() OVER (ORDER BY CheckTime ASC) AS Ordr
From Clock_Data 
LEFT JOIN Employees ON Employees.[ID Number] = Clock_Data.UserId 
    WHERE Employees.[First Name]='Hogen' AND Year(CheckTime) > YEAR(GetDate()) - 6 
    )
SELECT * FROM(
        SELECT 
         A.Ordr Number,a.[First Name],A.Checktime ct ,B.Checktime ct2,(DATEDIFF(MINUTE ,A.Checktime ,B.Checktime)/60) AS Hours
        FROM AUXILIERY_TBL AS A
        LEFT JOIN AUXILIERY_TBL AS B
               ON (A.Ordr=(B.Ordr-1))
   )c
WHERE c.Number % 2 <> 0

原始数据看起来像这样:

HOGEN                   2013-10-28 09:30:00
HOGEN                   2013-10-28 13:30:00
HOGEN                   2013-10-28 14:00:00
HOGEN                   2013-10-28 18:00:00
HOGEN                   2013-10-29 09:31:00
HOGEN                   2013-10-29 14:17:00
HOGEN                   2013-10-29 18:00:00
HOGEN                   2013-10-30 09:59:00
HOGEN                   2013-10-30 14:06:00
HOGEN                   2013-10-30 14:37:00
HOGEN                   2013-10-30 18:10:00

c.f。 SqlFiddle

1 个答案:

答案 0 :(得分:1)

您实际需要的是类似于SQLServer 2012+中提供的LAGLEAD分析函数,它根据指定的分区和相同的选择结果N行向上或向下获取一列。排序

在我的解决方案中,我循环遍历Clock_Data表,假设时间是检出时间,然后我模拟带有相关子查询的LAG函数,以根据每个签出时间让员工检查时间。肯定不是所有的时间都签出,所以我在子查询中添加了一个HAVING条件,以确定在同一天退房之前选择的入住时间是否是可用时间的奇数行。如果是,则返回时间,如果不返回null,那么我们可以跳过这些行。

请参阅SQL,请尽量不要使用带空格字符的列名...至少使用下划线字符:

select 
  ep.[First Name],
  z.CheckIn,
  z.CheckTime as CheckOut,
  DATEDIFF(MINUTE, z.CheckIn, z.CheckTime)/60 AS Hours
from
(
  select 
    cd.*, 
    (
      -- simulating a LAG(CheckTime, 1, null) with this subquery
      select max(CheckTime)
      from Clock_Data
      where 1=1
        -- select only times from the same employee
        and UserID = cd.UserID
        -- select only times before the checkout time (cd.CheckTime)
        and CheckTime <  cd.CheckTime
        -- select only times from the same year/month/day
        and CheckTime >= convert(date, cd.CheckTime)
      group by UserID
      -- as we're selecting the max(CheckTime) before a check out time
      -- in the same day, we're selecting the immediatelly previous row/time
      -- but this subquery must select an odd number of rows
      -- to ensure the max(CheckTime) is a check in time
      -- since the check in always occurs first, before a check out
      -- if it's an even number of rows, null is returned
      having count(*) % 2 = 1
    ) as checkIn
  from Clock_Data cd
) z
inner join Employees ep on 1=1
  and ep.[ID Number] = z.UserID
  -- and ep.[First Name] = 'Hogen'
where 1=1
  and z.checkIn is not null