2011-12-16 4 views
5

У меня есть таблица со следующими столбцами.Нужна помощь SQL Server Query, новичок

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 в столбце в той же строке. Идентификатор, как дни без простоя, должен быть нулевым. Можно ли это сделать так, как эта таблица настроена?

+0

Вопрос: для датировки, я думаю, это начальная дата, которая имеет значение, даже если разрешение после полуночи, верно? – fge

+3

Это, безусловно, возможно. Что вы пробовали? Вы должны посмотреть на документацию для DATEDIFF для начала. Кроме того, вам нужно будет решить, что делать с простоями, которые пересекаются с одного дня на другой. Это усложняет ситуацию, но это, безусловно, возможно. – paulbailey

ответ

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 

(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»

+2

FYI, как я решаю такие проблемы, как бумага и карандаш. Нарисуйте сегменты линии для всех возможных перекрывающихся и неперекрывающихся временных интервалов и соответствующим образом разработайте логику. –

+0

Ive никогда не использовал временную таблицу раньше! Как мне сказать, какой диапазон использовать? спасибо за помощь в этом. Я могу делать базовые sql-файлы, но этот продвинутый материал пролегает над моей головой! –

+0

Добавлен раздел о временной таблице –

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 

И я считаю, что это должно помочь вам добраться туда, где вы хотите.

Редактировать: Чтобы дни, в которых нет простоя, включенных в этот результат, необходимо сначала иметь список ВСЕХ дней, присутствующих в месяце. Вы берете этот список, и вы LEFT OUTER присоединяетесь к результату из вышеуказанного запроса (сначала вам нужно удалить ORDER BY).

1

Дело в заявлении Филиппа Келли не работает, хотя основной принцип заполнения таблицы темп с датами и левым соединением является истинным. Для моей версии я использовал одну и ту же переменную для запуска - дату ввода и количество дней для отчета.

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 строк, каждая с разницей в минуту бы подводить к 0 при использовании часов для DateDiff.

Смежные вопросы