每小时工人的SQL TSQL

时间:2015-07-18 04:40:51

标签: sql sql-server tsql sql-server-2014-express

我有一个带有指纹时间戳的日志,如下所示:

Usr   TimeStamp
-------------------------
 1    2015-07-01 08:01:00
 2    2015-07-01 08:05:00
 3    2015-07-01 08:07:00
 1    2015-07-01 10:05:00
 3    2015-07-01 11:00:00
 1    2015-07-01 12:01:00
 2    2015-07-01 13:03:00
 2    2015-07-01 14:02:00
 1    2015-07-01 16:03:00
 2    2015-07-01 18:04:00

我希望每小时的工人产量(四舍五入到最接近的小时) 理论输出应该是:

 7:00  0
 8:00  3
 9:00  3
 10:00 2
 11:00 1
 12:00 2
 13:00 1
 14:00 2
 15:00 2
 16:00 1
 17:00 1
 18:00 0
 19:00 0

任何人都可以考虑如何通过TSQL来解决这个问题吗?或者没有其他办法?

编辑:时间戳是不同用户的登录和注销。因此,早上8点,有3位用户登录,同样3位用户仍在上午9点工作。其中一人在上午10点离开。等

7 个答案:

答案 0 :(得分:0)

首先,您可以使用datepart获取以下日期的小时数,然后按用户分组

SELECT DATEPART(HOUR, GETDATE());

SQL Fiddle

SELECT Convert(varchar(5),DATEPART(HOUR, timestamp)) + ':00' as time,
count(usr) as users
from tbl
group by DATEPART(HOUR, timestamp)

答案 1 :(得分:0)

您需要datetime hour表来执行此操作。

注意:这只是显示查询应该如何工作一天的示例。将CTE替换为datetime hour表。在datetime hour表中,每个日期应以07:00:00小时开始,以19:00:00小时结束

如果您想要执行此操作超过一天,则可能必须在Cast(dt.date_time AS DATE)select中加入group by以区分属于哪一天的小时

WITH datetime_table 
     AS (SELECT '2015-07-01 07:00:00' AS date_time 
         UNION ALL 
         SELECT '2015-07-01 08:00:00' 
         UNION ALL 
         SELECT '2015-07-01 09:00:00' 
         UNION ALL 
         SELECT '2015-07-01 10:00:00' 
         UNION ALL 
         SELECT '2015-07-01 11:00:00' 
         UNION ALL 
         SELECT '2015-07-01 12:00:00' 
         UNION ALL 
         SELECT '2015-07-01 13:00:00' 
         UNION ALL 
         SELECT '2015-07-01 14:00:00' 
         UNION ALL 
         SELECT '2015-07-01 15:00:00' 
         UNION ALL 
         SELECT '2015-07-01 16:00:00' 
         UNION ALL 
         SELECT '2015-07-01 17:00:00' 
         UNION ALL 
         SELECT '2015-07-01 18:00:00' 
         UNION ALL 
         SELECT '2015-07-01 19:00:00') 
SELECT Datepart(hour, dt.date_time), 
       Hour_count=Count(t.id) 
FROM   datetime_table dt 
       LEFT OUTER JOIN Yourtable t 
                    ON Cast(t.dates AS DATE) = Cast(dt.date_time AS DATE) 
                       AND Datepart(hour, t.dates) = 
                           Datepart(hour, dt.date_time) 
GROUP  BY Datepart(hour, dt.date_time) 

答案 2 :(得分:0)

您只需要按小时和日期分组。请查看以下查询,希望这可以帮助您:

ng-click

答案 3 :(得分:0)

更难的部分是创建缺少数据的零。通常的方法是生成所有可能的#34;插槽的列表。然后对实际数据进行外连接。我假设您只想一次运行一天。

我的方法只是一个例子,因为它分别有两个表的交叉连接,分别有6和4行,6次4是24。

select f1.d * 6 + f0.d, coalesce(data.cnt, 0)
from
    (
        select 0 as d union all select 1 union all select 2 union all 
        select 3 union all select 4 union all select 5
    ) as f0,

    (
        select 0 as d union all select 1 union all
        select 2 union all select 3
    ) as f1
    left outer join
    (
        select
            cast(datepart(hh, TimeStamp) as varchar(2)) + ':00' as hr,
            count(*) as cnt
        from LOG
        group by datepart(hh, TimeStamp)
    ) as data
        on data.hr = f1.d * 6 + f0.d

答案 4 :(得分:0)

首先,你需要将时间四舍五入到最接近的时间

DATEADD(HOUR, DATEDIFF(HOUR, 0, DATEADD(MI, 30, TimeStamp)), 0)

如您所见,我们将原始时间(DATEADD(MI, 30, TimeStamp))添加30分钟 此方法也会将08:0408:0007:588:00向上舍入。 我假设有些工人可以提早开始工作

SELECT DATEADD(HOUR, DATEDIFF(HOUR, 0, DATEADD(MI, 30, TimeStamp)), 0) As FingertipTime
FROM Fingertips

如果您经常使用舍入时间戳

,则可以创建Computed column
ALTER TABLE Fingertips ADD RoundedTimeStamp AS (DATEADD(HOUR, DATEDIFF(HOUR, 0, DATEADD(MI, 30, TimeStamp)), 0));

为了将时间戳与工作时间常数进行比较,您可以找到不同的方法。我将使用TABLE类型的变量,其中我生成当天的工作时间
然后使用LEFT JOINGROUP BY获取时间戳数量

DECLARE @WorkHours TABLE(WorkHour DATETIME)
INSERT INTO @WorkHours (WorkHour) VALUES
('2015-07-01 07:00'),
('2015-07-01 08:00'),
('2015-07-01 09:00'),
('2015-07-01 10:00'),
('2015-07-01 11:00'),
('2015-07-01 12:00'),
('2015-07-01 13:00'),
('2015-07-01 14:00'),
('2015-07-01 15:00'),
('2015-07-01 16:00'),
('2015-07-01 17:00'),
('2015-07-01 18:00'),
('2015-07-01 19:00')

SELECT wh.Workhour
, COUNT(ft.TimeStamp) As Quantity
FROM @WorkHours wh
LEFT JOIN Fingertips ft ON ft.RoundedTimeStamp = wh.WorkHour
GROUP BY wh.WorkHour

选中此SQL Fiddle

答案 5 :(得分:0)

这是我最后的工作代码:

create table tsts(id int, dates datetime)
insert tsts values
(1 ,   '2015-07-01 08:01:00'),
(2 ,   '2015-07-01 08:05:00'),
(3 ,   '2015-07-01 08:07:00'),
(1 ,   '2015-07-01 10:05:00'),
(3 ,   '2015-07-01 11:00:00'),
(1 ,   '2015-07-01 12:01:00'),
(2 ,   '2015-07-01 13:03:00'),
(2 ,   '2015-07-01 14:02:00'),
(1 ,   '2015-07-01 16:03:00'),
(2 ,   '2015-07-01 18:04:00')

select horas.hora, isnull(sum(math) over(order by horas.hora rows unbounded preceding),0) as Employees from

(
select 0 as hora union all
select 1 as hora union all
select 2 as hora union all
select 3 as hora union all
select 4 as hora union all
select 5 as hora union all
select 6 as hora union all
select 7 as hora union all
select 8 as hora union all
select 9 as hora union all
select 10 as hora union all
select 11 as hora union all
select 12 as hora union all
select 13 as hora union all
select 14 as hora union all
select 15 as hora union all
select 16 as hora union all
select 17 as hora union all
select 18 as hora union all
select 19 as hora union all
select 20 as hora union all
select 21 as hora union all
select 22 as hora union all
select 23
) as horas

left outer join 
(
select hora, sum(math) as math from 
    (
    select id, hora, iif(rowid%2 = 1,1,-1) math from 
        (
        select row_number() over (partition by id order by id, dates) as rowid, id, datepart(hh,dateadd(mi, 30, dates)) as hora from tsts 
        ) as Q1
    ) as Q2
group by hora
) as Q3 

on horas.hora = Q3.hora

SQL Fiddle

答案 6 :(得分:0)

许多单独的部件必须粘在一起才能完成。 首先进行舍入,可以轻松完成日期+ 30分钟的小时部分。然后确定开始和结束记录。如果没有字段来指示这一点并假设第一次出现的是登录或开始,则可以使用row_number并使用奇数作为开始记录。

然后开始和结束必须耦合,在sql server 2012及更高版本中,这可以通过lead函数轻松完成

要获取缺失的小时,必须创建一个包含所有小时的序列。有几个选项(良好的链接here),但我喜欢在一个表上使用row_number的方法,该表肯定包含足够的行(有一个适当的列用于order by),例如在sys.all_objects中使用的链接。这样,小时7到19可以创建为:select top 13 ROW_NUMBER() over (order by object_id) + 6 [Hour] from sys.all_objects

如果只有一个要检查的日期,则查询可以在时间戳指纹的小时上简单地左连接。如果有更多日期,则可以创建第二个序列,交叉应用于时间以获取所有日期。假设一个日期,最终代码将是:

declare @t table(Usr int, [timestamp] datetime)

insert @t values
(1  ,   '2015-07-01 08:01:00'),
(2 ,   '2015-07-01 08:05:00'),
(3 ,   '2015-07-01 08:07:00'),
(1 ,   '2015-07-01 10:05:00'),
(3 ,   '2015-07-01 11:00:00'),
(1 ,   '2015-07-01 12:01:00'),
(2 ,   '2015-07-01 13:03:00'),
(2 ,   '2015-07-01 14:02:00'),
(1 ,  '2015-07-01 16:03:00'),
(2 ,   '2015-07-01 18:04:00'),
(2 ,   '2015-07-01 18:04:00')

;with usrHours as
(
    select Usr, datepart(hour, DATEADD(minute,30, times.timestamp)) [Hour] --convert all times to the rounded hour (rounding by adding 30 minutes)
     , ROW_NUMBER() over (partition by usr order by [timestamp] ) rnr
    from @t times --@t should be your logging table
), startend as --get next (end) hour by using lead
(
    select Usr, [hour] StartHour , LEAD([Hour]) over (partition by usr order by rnr) NextHour  ,rnr
    from usrHours
),hours as --sequence of hours 7 to 19
(
    select top 13 ROW_NUMBER() over (order by object_id) + 6 [Hour] from sys.all_objects
)

select cast([Hour] as varchar) + ':00' [Hour], COUNT(startend.usr) Users 
from hours --sequence is leading
left join startend on hours.Hour between startend.StartHour and startend.NextHour 
                                and rnr % 2  = 1 --every odd row number is a start time
group by Hours.hour