SQL TSQL 工人每小时
SQL TSQL for Workers per Hour
我有一个带有指纹时间戳的日志如下:
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
任何人都可以考虑如何通过 SQL 或如果没有其他方法,通过 TSQL 来解决这个问题?
编辑:时间戳是不同用户的登录和注销。因此,在早上 8 点,有 3 个用户登录,而同样的 3 个用户在上午 9 点仍在工作。其中一个在上午 10 点离开。等等
首先,您可以使用 datepart 获取以下日期的小时数,然后按用户分组
SELECT DATEPART(HOUR, GETDATE());
SELECT Convert(varchar(5),DATEPART(HOUR, timestamp)) + ':00' as time,
count(usr) as users
from tbl
group by DATEPART(HOUR, timestamp)
您需要 datetime hour
table 才能执行此操作。
注意:这只是一个示例,展示了查询在一天内应该如何工作。将 CTE
替换为 datetime hour
table。在 datetime hour
table 中,每个日期都应以 07:00:00
小时开始并以 19:00:00
小时结束
如果您想在一天以上执行此操作,则可能需要在 select
和 group by
中包含 Cast(dt.date_time AS DATE)
以区分该小时属于哪一天
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)
您只需按时间和日期分组。检查以下查询,希望对您有所帮助:
Create table #t1
(
usr int,
timelog datetime
)
Insert into #t1 values(1, '2015-07-01 08:01:00')
Insert into #t1 values(2, '2015-07-01 08:05:00')
Insert into #t1 values(3, '2015-07-01 08:07:00')
Insert into #t1 values(1, '2015-07-01 10:05:00')
Insert into #t1 values(3, '2015-07-01 11:00:00')
Insert into #t1 values(1, '2015-07-01 12:01:00')
Insert into #t1 values(2, '2015-07-01 13:03:00')
Insert into #t1 values(2, '2015-07-01 14:02:00')
Insert into #t1 values(1, '2015-07-01 16:03:00')
Insert into #t1 values(2, '2015-07-01 18:04:00')
Select cast(timelog as varchar(11)) as LogDate, Datepart(hour, timelog) as LogTime, count(usr) as UserCount from #t1
Group by Datepart(hour, timelog), cast(timelog as varchar(11))
更难的部分是在数据缺失的地方创建零。通常的方法是生成所有可能 "slots" 的列表,然后对实际数据进行外部连接。我假设您一次只想 运行 这一天。
我的方法(这只是一个示例)之所以有效,是因为它交叉连接了分别具有 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
首先你需要将时间四舍五入到最接近的小时
DATEADD(HOUR, DATEDIFF(HOUR, 0, DATEADD(MI, 30, TimeStamp)), 0)
正如你首先看到的,我们在原来的时间基础上增加了 30 分钟(DATEADD(MI, 30, TimeStamp))
这种方法也会将 08:04
舍入到 08:00
或将 07:58
舍入到 8: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 JOIN
和 GROUP 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
这是我的最终工作代码:
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
许多独立的部分必须粘合在一起才能完成。
第一舍入,这很容易通过获取日期的小时部分 + 30 分钟来完成。然后确定开始和结束记录。如果没有字段表明这一点并且假设一天的第一次出现是登录或开始,您可以使用 row_number 并使用奇数作为开始记录。
然后必须耦合开始和结束,在 sql 服务器 2012 和更高版本中,这可以使用 lead
函数
轻松完成
要获取缺失的小时数,必须创建包含所有小时数的序列。有几个选项(好的 link here),但我喜欢在 table 上使用 row_number 的方法,它肯定包含足够的行(有一个适当的列用于order by),如sys.all_objects中使用的link。这样,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
我有一个带有指纹时间戳的日志如下:
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
任何人都可以考虑如何通过 SQL 或如果没有其他方法,通过 TSQL 来解决这个问题?
编辑:时间戳是不同用户的登录和注销。因此,在早上 8 点,有 3 个用户登录,而同样的 3 个用户在上午 9 点仍在工作。其中一个在上午 10 点离开。等等
首先,您可以使用 datepart 获取以下日期的小时数,然后按用户分组
SELECT DATEPART(HOUR, GETDATE());
SELECT Convert(varchar(5),DATEPART(HOUR, timestamp)) + ':00' as time,
count(usr) as users
from tbl
group by DATEPART(HOUR, timestamp)
您需要 datetime hour
table 才能执行此操作。
注意:这只是一个示例,展示了查询在一天内应该如何工作。将 CTE
替换为 datetime hour
table。在 datetime hour
table 中,每个日期都应以 07:00:00
小时开始并以 19:00:00
小时结束
如果您想在一天以上执行此操作,则可能需要在 select
和 group by
中包含 Cast(dt.date_time AS DATE)
以区分该小时属于哪一天
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)
您只需按时间和日期分组。检查以下查询,希望对您有所帮助:
Create table #t1
(
usr int,
timelog datetime
)
Insert into #t1 values(1, '2015-07-01 08:01:00')
Insert into #t1 values(2, '2015-07-01 08:05:00')
Insert into #t1 values(3, '2015-07-01 08:07:00')
Insert into #t1 values(1, '2015-07-01 10:05:00')
Insert into #t1 values(3, '2015-07-01 11:00:00')
Insert into #t1 values(1, '2015-07-01 12:01:00')
Insert into #t1 values(2, '2015-07-01 13:03:00')
Insert into #t1 values(2, '2015-07-01 14:02:00')
Insert into #t1 values(1, '2015-07-01 16:03:00')
Insert into #t1 values(2, '2015-07-01 18:04:00')
Select cast(timelog as varchar(11)) as LogDate, Datepart(hour, timelog) as LogTime, count(usr) as UserCount from #t1
Group by Datepart(hour, timelog), cast(timelog as varchar(11))
更难的部分是在数据缺失的地方创建零。通常的方法是生成所有可能 "slots" 的列表,然后对实际数据进行外部连接。我假设您一次只想 运行 这一天。
我的方法(这只是一个示例)之所以有效,是因为它交叉连接了分别具有 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
首先你需要将时间四舍五入到最接近的小时
DATEADD(HOUR, DATEDIFF(HOUR, 0, DATEADD(MI, 30, TimeStamp)), 0)
正如你首先看到的,我们在原来的时间基础上增加了 30 分钟(DATEADD(MI, 30, TimeStamp))
这种方法也会将 08:04
舍入到 08:00
或将 07:58
舍入到 8: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 JOIN
和 GROUP 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
这是我的最终工作代码:
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
许多独立的部分必须粘合在一起才能完成。 第一舍入,这很容易通过获取日期的小时部分 + 30 分钟来完成。然后确定开始和结束记录。如果没有字段表明这一点并且假设一天的第一次出现是登录或开始,您可以使用 row_number 并使用奇数作为开始记录。
然后必须耦合开始和结束,在 sql 服务器 2012 和更高版本中,这可以使用 lead
函数
要获取缺失的小时数,必须创建包含所有小时数的序列。有几个选项(好的 link here),但我喜欢在 table 上使用 row_number 的方法,它肯定包含足够的行(有一个适当的列用于order by),如sys.all_objects中使用的link。这样,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