我有一个包含以下列的表。
id,
compid(used to identify a piece of equipment),
startalarmdate,
endalarmdate
当一台设备进入报警状态时,我将compid和startalarmdate(带有当前时间)插入表中,当设备报警时,我在endalarmdate列的同一行填写空值当前时间。
所以我在表格中得到了类似的东西
417,6,Sun Oct 30 18:48:17 CDT 2011,Mon Oct 31 09:49:21 CDT 2011
422,6,Mon Oct 31 10:19:19 CDT 2011,Mon Oct 31 12:19:22 CDT 2011
427,6,Mon Oct 31 20:19:56 CDT 2011,Tue Nov 01 09:50:59 CDT 2011
429,6,Tue Nov 01 21:51:41 CDT 2011,Wed Nov 02 09:52:37 CDT 2011
432,6,Wed Nov 02 21:23:23 CDT 2011,Fri Nov 04 16:26:29 CDT 2011
我能够构建一个查询,为每个事件提供一个完整的停机时间(以小时为单位),但我现在要做的是构建一个查询,让我每个月的每一天都有一个停机时间。我喜欢它一直到左边的compid,然后在同一行的一列中的compid右边的月份的每一天。我喜欢没有停机的日子是空的。是否可以按照此表的设置方式执行此操作?
答案 0 :(得分:1)
步骤1:设置一个临时表,其中包含您想要总计的所需“时间块”。这些块可以用于任何时间范围;在你的例子中,它将是一个月中每天(24小时)的一个条目。
CREATE TABLE #TimeRanges
(
RangeStart datetime not null
,RangeEnd datetime not null
)
在数据的左外连接上确保每个时间段(日)至少有一行,即使当天没有发生警报:
SELECT
tr.RangeStart -- Use start of each time block to identify the block
,md.CompId -- With left outer join, will be 0 or more rows for each block
,sum(datediff(hh
,case
when tr.RangeStart > md.StartAlarmDate then tr.RangeStart
else md.StartAlarmDate
end
,case
when tr.RangeEnd > md.EndAlarmDate then tr.RangeEnd
else md.EndAlarmDate
end)) HoursInRange
from #TimeRanges tr
left outer join MyData md
on md.StartAlarmDate < tr.RangeEnd
and md.EndAlarmDate > tr.From
group by
tr.RangeStart
,md.CompId
(我无法测试此代码,可能需要进行一些调试 - 但概念是可靠的。我会让您担心舍入部分小时数,以及是否需要&gt;和&lt ;,或&gt; =和&lt; =(如果警报开始和/或在与块边界完全相同的时间点结束,事情可能会变得棘手)。
修改/附加物强>
这是设置例程中使用的临时表的一种相当基本的方法(这段代码,我测试过):
-- Set up and initialize some variables
DECLARE
@FirstDay datetime
,@NumberOfDays int
SET @FirstDay = 'Oct 1, 2011' -- Without time, this makes it "midnight the morning of" that day
SET @NumberOfDays = 91 -- Through Dec 31
-- Creates a temporary table that will persist until it is dropped or the connection is closed
CREATE TABLE #TimeRanges
(
RangeStart datetime not null
,RangeEnd datetime not null
)
-- The order in which you add rows to the table is irrelevant. By adding from last to first, I
-- only have to fuss around with one variable, instead of two (the counter and the end-point)
WHILE @NumberOfDays >= 0
BEGIN
INSERT #TimeRanges (RangeStart, RangeEnd)
values ( dateadd(dd, @NumberOfDays, @FirstDay) -- Start of day
,dateadd(dd, @NumberOfDays + 1, @FirstDay)) -- Start of the next day
SET @NumberOfDays = @NumberOfDays - 1
END
-- Review results
SELECT *
from #TimeRanges
order by RangeStart
-- Not necessary, but doesn't hurt, especially when testing code
DROP TABLE #TimeRanges
请注意,通过使RangeEnd成为第二天的开始,你必须小心你的greaterthans和lessthans。细节在那里会非常挑剔和挑剔,你会想做很多边缘情况测试(如果警报开始或结束,恰好在2011年12月16日00:00.000)。我会继续使用它,因为整体来说它比编码更简单,比如“Dec 16,2011 23:59.997”
答案 1 :(得分:1)
正如@paulbailey所提到的,您希望使用DATEDIFF函数来获取停机时间。
提取日期和停机时间(我正在添加您可能需要的更多列)..
SELECT compid,
YEAR(startalarmdate) AS [Year],
MONTH(startalarmdate) AS [Month],
DAY(startalarmdate) AS [Day],
DATEDIFF(ss, startalarmdate, endalarmdate) AS DowntimeInSeconds --You will need to convert thid later to the format you wish to use
FROM YourTable
/* WHERE CLAUSE - Most probably a date range */
现在,这可以为您提供停机时间的每秒停机时间。
通过按天分组并减少停机时间(再次添加您可能需要的更多列),可以轻松获得每天的停机时间。
SELECT compid,
[Year],
[Month],
[Day],
SUM(DowntimeInSeconds) AS TotalDowntimeInSeconds
FROM (SELECT compid,
YEAR(startalarmdate) AS [Year],
MONTH(startalarmdate) AS [Month],
DAY(startalarmdate) AS [Day],
DATEDIFF(ss, startalarmdate, endalarmdate) AS DowntimeInSeconds --You will need to convert thid later to the format you wish to use
FROM YourTable
/* WHERE CLAUSE - Most probably a date range */) AS GetDowntimes
GROUP BY compid, [Year], [Month], [Day]
ORDER BY [Year], [Month], [Day], compid
我相信这可以帮助你达到你想要的目的。
编辑: 要使此结果中包含没有停机时间的日期,您需要首先列出一个月内存在的所有日期。你拿这个列表然后你左上外连接上面的查询结果(你必须先删除ORDER BY)。
答案 2 :(得分:1)
Philip Kelley的回答中的案例陈述不起作用,尽管填充临时表的日期和左加入的主要原则是正确的。对于我的版本,我使用相同的变量开始 - 输入日期和报告的天数。
DECLARE @StartDate DATETIME, @Days INT
SELECT @StartDate = GETDATE(),
@Days = 5
-- REMOVE ANY TIME FROM THE STARTDATE
SET @StartDate = DATEADD(DAY, 0, DATEDIFF(DAY, 0, @StartDate))
-- CREATE THE TEMP TABLE OF DATES USING THE SAME METHODOLOGY
DECLARE @Dates TABLE (AlarmDate SMALLDATETIME NOT NULL PRIMARY KEY)
WHILE (@Days > 0)
BEGIN
INSERT @Dates VALUES (DATEADD(DAY, @Days, @StartDate))
SET @Days = @Days - 1
END
-- NOW SELECT THE DATA
SELECT AlarmDate,
CompID,
CONVERT(DECIMAL(10, 2), ISNULL(SUM(DownTime), 0) / 3600.0) [DownTime]
FROM @Dates
LEFT JOIN
( SELECT DATEADD(DAY, 0, DATEDIFF(DAY, 0, StartAlarmDate)) [StartAlarmDate],
CompID,
DATEDIFF(SECOND, StartAlarmDate, CASE WHEN EndAlarmDate >= DATEADD(DAY, 1, DATEDIFF(DAY, 0, StartAlarmDate)) THEN DATEADD(DAY, 1, DATEDIFF(DAY, 0, StartAlarmDate)) ELSE EndAlarmDate END) [DownTime]
FROM [yourTable]
UNION ALL
SELECT DATEADD(DAY, 0, DATEDIFF(DAY, 0, EndAlarmDate)) [Date],
CompID,
DATEDIFF(SECOND, DATEADD(DAY, 1, DATEDIFF(DAY, 0, StartAlarmDate)), EndAlarmDate) [DownTime]
FROM [yourTable]
WHERE EndAlarmDate >= DATEADD(DAY, 1, DATEDIFF(DAY, 0, StartAlarmDate))
) data
ON StartAlarmDate = AlarmDate
GROUP BY AlarmDate, CompID
我已经将日期差异的秒数除以3600.0,在秒数总计为60行后,每分钟差异为1分钟,当使用小时数时,总和为0。