2011-12-16 38 views
5

Tôi có một bảng có các cột sau đây.Cần trợ giúp Truy vấn SQL Server, Người mới

id, 
compid(used to identify a piece of equipment), 
startalarmdate, 
endalarmdate 

khi một phần của thiết bị đi vào báo động, tôi chèn compid và startalarmdate (với thời điểm hiện tại) vào bảng và khi thiết bị đi ra báo động I điền vào null trong cùng hàng đó trong cột endalarmdate với thời gian hiện tại.

vì vậy tôi kết thúc với một cái gì đó như thế này trong bảng

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 

tôi đã có thể xây dựng một truy vấn mà mang lại cho tôi một tổng thời gian chết trong giờ đối với mỗi sự kiện, nhưng những gì tôi muốn làm bây giờ là xây dựng một truy vấn mang lại cho tôi tổng số giờ trong thời gian ngừng hoạt động cho mỗi ngày trong một tháng. Id giống như nó có phần biên dịch tất cả các cách bên trái, sau đó có mỗi ngày trong tháng ở bên phải của bản dịch trong một cột trên cùng một hàng. Id như những ngày không có thời gian chết để được null. Có thể làm điều đó với cách thiết lập bảng này không?

+0

Câu hỏi: trong một ngày, tôi đoán đó là ngày bắt đầu có vấn đề ngay cả khi độ phân giải sau nửa đêm, phải không? – fge

+3

Chắc chắn là có thể. Bạn đã thử cái gì? Bạn nên xem tài liệu cho DATEDIFF để bắt đầu. Ngoài ra, bạn sẽ cần phải quyết định phải làm gì với thời gian chết đi từ ngày này sang ngày khác. Điều đó làm cho mọi thứ phức tạp hơn, nhưng chắc chắn là có thể thực hiện được. – paulbailey

Trả lời

1

Bước 1: thiết lập bảng tạm thời có chứa "khoảng thời gian" mong muốn mà bạn muốn tổng cộng. Những khối này có thể ở bất kỳ khoảng thời gian nào; trong ví dụ của bạn, nó sẽ là một mục nhập cho ngày nào đó (khoảng thời gian 24 giờ) trong tháng.

CREATE TABLE #TimeRanges 
(
    RangeStart datetime not null 
    ,RangeEnd datetime not null 
) 

Left-outer-gia nhập bảng này trên dữ liệu của bạn đảm bảo bạn sẽ có được ít nhất một dòng cho mỗi khối thời gian (ngày), thậm chí nếu có không có báo động xảy ra ngày hôm đó:

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 

(Tôi Tôi không cho phép bạn lo lắng về làm tròn một phần giờ, và cho dù bạn muốn> và <, hoặc> = và < = (mọi thứ có thể gặp khó khăn nếu báo thức bắt đầu và/hoặc kết thúc tại cùng một thời điểm chính xác dưới dạng ranh giới khối).


Edit/Addenda

Đây là một cách khá cơ bản để thiết lập bảng tạm thời được sử dụng trong thói quen (mã này, tôi đã kiểm tra):

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

Lưu ý rằng bằng cách làm cho RangeEnd sự bắt đầu ngày hôm sau, bạn phải cẩn thận với những người lớn và lessthans của bạn. Các chi tiết có thể nhận được rất khó tính và cầu kỳ ở đó, và bạn sẽ muốn làm rất nhiều thử nghiệm cạnh trường hợp (nếu báo thức bắt đầu, hoặc kết thúc, chính xác vào ngày 16 tháng 12 2011 00: 00.000). Tôi muốn đi với điều đó, bởi vì tổng thể nó đơn giản hơn để mã hóa cho hơn cho rác như 'Dec 16, 2011 23: 59.997'

+2

FYI, cách tôi giải quyết các vấn đề như thế này là bằng giấy và bút chì. Vẽ các đoạn đường cho tất cả các khoảng thời gian chồng chéo và không trùng lặp có thể chồng lên nhau và tính toán logic theo đó. –

+0

Tôi chưa bao giờ sử dụng bảng tạm thời trước đây! Làm cách nào để tôi biết phạm vi sử dụng? Cảm ơn về sự giúp đỡ này. Tôi có thể làm những thứ sql cơ bản nhưng công cụ tiên tiến này là cách trên đầu của tôi! –

+0

Đã thêm phần trên bảng tạm thời –

1

Như đã đề cập bởi @paulbailey, bạn muốn sử dụng hàm DATEDIFF để có được lượng thời gian chết .

Để trích xuất các ngày và thời gian thời gian chết (Tôi bổ sung thêm một chút cột mà bạn có thể cần) ..

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 */ 

Bây giờ này cung cấp cho bạn thời gian chết trong vài giây cho mỗi ngày mà đã có một thời gian chết.

Để có được lượng thời gian ngừng hoạt động mỗi ngày dễ dàng như nhóm theo ngày và Tổng thời gian ngừng hoạt động (lại thêm nhiều cột mà bạn có thể cần).

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 

Và tôi tin điều này sẽ giúp bạn có được nơi bạn muốn.

Chỉnh sửa: Để có những ngày không có thời gian chết trong kết quả này, trước tiên bạn cần có danh sách TẤT CẢ các ngày có trong tháng. Bạn lấy danh sách này và bạn LEFT OUTER JOIN kết quả từ truy vấn trên (bạn sẽ phải loại bỏ ORDER BY trước).

1

Tuyên bố trường hợp trong câu trả lời của Philip Kelley không hoạt động, mặc dù hiệu trưởng chính của việc điền bảng tạm thời có ngày tháng và tham gia trái là đúng. Đối với phiên bản của tôi, tôi đã sử dụng cùng một biến để bắt đầu - ngày nhập và số ngày cần báo cáo.

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 

Tôi có giây đã qua sử dụng cho diff cập nhật và chia 3600.0 sau giây đã được tóm tắt như 60 hàng đều có một sự khác biệt của một phút sẽ tổng hợp để 0 khi sử dụng giờ cho một datediff.

Các vấn đề liên quan