我正在跟踪机器状态,可以是0,1和2, 并使用time_stamp将该数据存储在sql表中。 我在sql server中有下一个字段的表: ID(INT) TIME_STAMP(日期时间) MACHINE_STATE(INT)
机器状态与机器状态有关:
machine_state = 0 -machine stooped
machine_state = 1台带报警器的机器
machine_state = 2台机器运行
现在我想计算每个班次中每个州的机器有多长。 转移是
我的问题是如何计算每个机器状态的时间(sum_time_0,sum_time_1,sum_time_2)并按班次分组。我想以秒为单位计算时间,然后转换为分钟。
为了获得更好的图片,我确实导出了表格的一部分
EXPORT_TABLE
id time_stamp machine_state
1623 6.10.2009 17:09:00 1
1624 6.10.2009 17:17:00 2
1625 6.10.2009 17:17:00 1
1626 6.10.2009 17:17:00 2
1627 6.10.2009 17:18:00 1
1628 6.10.2009 17:18:00 2
1629 6.10.2009 18:04:00 1
1630 6.10.2009 18:06:00 2
1631 6.10.2009 18:07:00 1
1632 6.10.2009 18:12:00 2
1633 6.10.2009 18:28:00 1
1634 6.10.2009 18:28:00 2
1635 6.10.2009 19:16:00 1
1636 6.10.2009 19:21:00 2
1637 6.10.2009 19:49:00 1
1638 6.10.2009 20:23:00 2
任何建议都会有所帮助。 提前谢谢。
答案 0 :(得分:2)
您可以为每一行加入下一个机器状态,然后按州分组,并对时间差进行求和......
create table #t(id int identity(1,1), ts datetime, ms tinyint);
insert into #t
select '6.10.2009 17:09:00', 1
union select '6.10.2009 17:17:00', 2
union select '6.10.2009 17:17:00', 1
union select '6.10.2009 17:17:00', 2
union select '6.10.2009 17:18:00', 1
union select '6.10.2009 17:18:00', 2
union select '6.10.2009 18:04:00', 1
union select '6.10.2009 18:06:00', 2
union select '6.10.2009 18:07:00', 1
union select '6.10.2009 18:12:00', 2
union select '6.10.2009 18:28:00', 1
union select '6.10.2009 18:28:00', 2
union select '6.10.2009 19:16:00', 1
union select '6.10.2009 19:21:00', 2
union select '6.10.2009 19:49:00', 1
union select '6.10.2009 20:23:00', 2
select
t.ms,
sum(datediff(mi, t.ts, tn.ts)) as total_mintues
from
#t t
inner join #t tn on
tn.id = (select top 1 t2.id
from #t t2
where t2.id > t.id and t2.ms <> t.ms
order by t2.id)
group by
t.ms
/*
ms total_mintues
1 54
2 140
*/
drop table #t
答案 1 :(得分:1)
这是我如何做的概述。我做了一些假设,这些假设可能无效或不适用于你的情况,所以我没有把所有的东西编码。
首先,我将问题分解为块:一次计算一个班次的数据。 (我猜你每天运行一次,或者每周运行一次。)
我会将其实现为具有两个参数的存储过程:
建立两个“完整”日期时间,一个用于班次开始,一个用于结束。例如,如果@ShiftDate ='2009年10月22日'和@Shift = 2,你就得到了
创建临时表以保存我们将要分析的数据子集。像这样填充它:
接下来,检查@ShiftStart和@ShiftStop是否存在条目。如果没有这样的条目:
您需要这些条目来获取班次开始与该班次中第一个记录条目之间的时间数据,并且在班次结束时也是如此。
此时,项目按时间排序,具有均匀递增的计数器列(1,2,3)。假设以上所有,以下查询 应该返回您正在寻找的数据:
SELECT
et.machine_state
,sum(datediff(ss, et.time_stamp, thru.time_stamp)) TotalSeconds
,sum(datediff(ss, et.time_stamp, thru.time_stamp)) / 60 TotalMinutes
from #EXPORT_TABLE et
inner join #EXPORT_TABLE thru
on thru.id = et.id + 1
group by et.machine_state
order by et.machine_state
注意:
这只是一个框架。我认为你可以将它扩展到你必须处理的任何条件。
答案 2 :(得分:1)
您可以使用独占联接查找上一行:
select
State = prev.ms,
MinutesInState = sum(datediff(mi, prev.ts, cur.ts))
from @t cur
inner join @t prev
on prev.id < cur.id
left join @t inbetween
on prev.id < inbetween.id
and inbetween.id < cur.id
where inbetween.id is null
group by prev.ms
然后查询按机器状态分组。结果与其他答案不同,我很好奇哪一个是对的!
State MinutesInState
1 54
2 140
以下是我使用的示例数据:
declare @t table (id int identity(1,1), ts datetime, ms tinyint);
insert into @t
select '6.10.2009 17:09:00', 1
union select '6.10.2009 17:17:00', 2
union select '6.10.2009 17:17:00', 1
union select '6.10.2009 17:17:00', 2
union select '6.10.2009 17:18:00', 1
union select '6.10.2009 17:18:00', 2
union select '6.10.2009 18:04:00', 1
union select '6.10.2009 18:06:00', 2
union select '6.10.2009 18:07:00', 1
union select '6.10.2009 18:12:00', 2
union select '6.10.2009 18:28:00', 1
union select '6.10.2009 18:28:00', 2
union select '6.10.2009 19:16:00', 1
union select '6.10.2009 19:21:00', 2
union select '6.10.2009 19:49:00', 1
union select '6.10.2009 20:23:00', 2
答案 3 :(得分:1)
如果你只想要快速和肮脏,这样做:
select curr.*, prev.*
from EXPORT_TABLE curr
outer apply (
select top 1 * from EXPORT_TABLE prev
where curr.time_stamp > prev.time_stamp
order by time_stamp desc, id desc
) prev
从那里开始。
但是这个方法,以及这个涉及非等值连接的页面上的一些类似方法,不能很好地扩展。要处理大量数据,我们必须使用不同的技术。
您的ID显示为顺序。是吗?这可能很有用。如果没有,我们应该创建一个。
if object_id('tempdb..#pass1') is not null drop table #pass1
create table #pass1 (
id int
, time_stamp smalldatetime
, machine_state tinyint
, seqno int primary key -- this is important
)
insert #pass1
select
id
, time_stamp
, machine_state
, seqno = row_number() over (order by time_stamp, id)
from EXPORT_TABLE
一旦我们有序列ID,我们就可以等同于它:
if object_id('tempdb..#pass2') is not null drop table #pass2
create table #pass2 (
id int
, time_stamp smalldatetime
, machine_state tinyint
, seqno int primary key
, time_stamp_prev smalldatetime
)
insert #pass2
select
id
, time_stamp
, machine_state
, seqno
, time_stamp_prev = b.time_stamp
from #pass1 a
left join #pass1 b on a.seqno = b.seqno + 1
从这里开始,您的查询应该只是写自己。但要注意与班次重叠的机器状态。
这种方法虽然看起来很昂贵,但随着音量的增加会很好。您订购一次数据,然后加入一次。如果id是顺序的,你可以跳过第一步,确保id上有一个聚簇主键,并加入id而不是seqno。
如果你有真正大量数据,那么你可以这样做:
if object_id('tempdb..#export_table') is not null drop table #export_table
create table #pass1 (
id int
, time_stamp smalldatetime
, machine_state tinyint
, seqno int primary key -- ensures proper ordering for the UPDATE
, time_stamp_prev smalldatetime
)
insert #export_table (
id
, time_stamp
, machine_state
, seqno
)
select
id
, time_stamp
, machine_state
, seqno = row_number() over (order by time_stamp, id)
from EXPORT_TABLE
-- do some magic
declare @time_stamp smalldatetime
update #export_table set
time_stamp_prev = @time_stamp
, @time_stamp = time_stamp
这将胜过所有其他方法。如果你的id是正确的顺序(它不必是顺序的,只是按照正确的顺序),你可以跳过第一步并在id上定义一个聚集索引,如果它还没有在那里。
答案 4 :(得分:0)
你可以这样做:
select t1.time_stamp time_start, t2.time_stamp time_finish, t1.machine_state
from EXPORT_TABLE t1, EXPORT_TABLE t2
where t2.time_stamp = (select min(time_stamp) from @table where time_stamp > t1.time_stamp)
这将返回一行中的间隔,之后很容易计算每个州的累积时间。
您还可以查看this question。它似乎与你的几乎相似。
答案 5 :(得分:0)
感谢您的帮助。 我很惊讶细节是什么答案。 我会测试你的解决方案并通知你结果。 我再次对细节回答感到非常惊讶。
我测试了第一部分(机器状态0,1 i 2的总和时间),这没关系。 现在我将测试其余部分答案。
对我来说最大的问题是在班次过渡期间分时。 例: '6.10.2009 16:30:00',1 '6.10.2009 17:30:00',2 '6.10.2009 19:16:00',1
在16:30到17:00之间,机器处于状态1,那时我必须添加到班次1,并且17:00到17:30之间的时间机器处于状态1并且那时我必须添加到班次2。
但首先我会回答你,看看你是否已经为此做出了解决方案。
再次感谢
答案 6 :(得分:0)
CREATE PROCEDURE dbo.final @shiftdate datetime, @shift int
AS
BEGIN
DECLARE
@shiftstart as datetime ,
@shiftstop as datetime,
@date_m as varchar(33),
@timestart as char(8),
@smjena as int,
@ms_prev as int,
@t_rad as int,
@t_stop as int,
@t_alarm as int
if @shift = 1
begin
set @timestart = '08:00:00'
set @smjena=9
end
if @shift = 2
begin
set @timestart = '17:00:00'
set @smjena=8
end
if @shift = 3
begin
set @timestart = '01:00:00'
set @smjena=7
end
SELECT @date_m = convert(varchar, @shiftdate, 104) + ' ' + convert(varchar, @timestart, 114)
set @shiftstart = convert(datetime,@date_m,104)
select @shiftstop = dateadd(hh,@smjena,@shiftstart)
create table #t(id int identity(1,1), ts datetime, ms tinyint);
insert #t select time_stamp, stanje_stroja from perini where perini.time_stamp between @shiftstart and @shiftstop order by perini.time_stamp
if (select count(#t.id) from #t where #t.ts=@shiftstart)= 0
BEGIN
if (select count(perini.id) from perini where time_stamp < @shiftstart) > 0
begin
set @ms_prev = (select top 1 stanje_stroja from perini where time_stamp<@shiftstart order by time_stamp asc)
insert #t values (@shiftstart,@ms_prev)
end
end
if (select count(#t.id) from #t where #t.ts=@shiftstop)= 0
BEGIN
if (select count(perini.id) from perini where time_stamp > @shiftstop) > 0
begin
set @ms_prev = (select top 1 stanje_stroja from perini where time_stamp>@shiftstop order by time_stamp asc)
insert #t values (@shiftstop,@ms_prev)
end
end
select * into #t1 from #t where 1=2
insert into #t1 select ts, ms from #t order by ts
create table #t3(stanje int, trajanje int)
insert into #t3 select a.ms as stanje, convert(int,sum(datediff(ss,b.ts, a.ts))/60) as trajanje from
#t1 a left join #t1 b on a.id = b.id + 1
group by a.ms
set @t_rad = (select trajanje from #t3 where stanje = 2)
set @t_alarm = (select trajanje from #t3 where stanje = 1)
set @t_stop = (select trajanje from #t3 where stanje = 0)
insert into perini_smjene_new (smjena,t_rad, t_stop, t_alarm, time_stamp) values (@shift,@t_rad,@t_stop, @t_alarm, convert(datetime, @shiftdate, 103))
select * from #t3
END