Шаг 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
(I не могу проверить этот код, может потребоваться некоторая отладка, но концепция прочная. Я позволю вам беспокоиться о округлении частичных часов и хотите ли вы> и <, или> = и < = (вещи могут оказаться сложными если тревога начинается и/или заканчивается в тот же момент времени, что и граница блока).
Редактировать/Addenda
Вот довольно простой способ настроить временную таблицу, используемую в обычной (этот код, я проверил):
-- 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 в начало следующего дня, вы должны быть осторожны с вашими большими людьми и лесшанами. Детали могут быть очень искушенными и суетливыми, и вы захотите сделать много краевых испытаний (что, если будильник начинается или заканчивается, ровно в 16 декабря 2011 00: 00.000). Я бы пошел с этим, потому что в целом это проще для кода, чем для мусора, как «Dec 16, 2011 23: 59.997»
Вопрос: для датировки, я думаю, это начальная дата, которая имеет значение, даже если разрешение после полуночи, верно? – fge
Это, безусловно, возможно. Что вы пробовали? Вы должны посмотреть на документацию для DATEDIFF для начала. Кроме того, вам нужно будет решить, что делать с простоями, которые пересекаются с одного дня на другой. Это усложняет ситуацию, но это, безусловно, возможно. – paulbailey