简体   繁体   English

SQL:按日期报告数据

[英]SQL: Reporting on data by date

I have a table I am trying to generate reports from. 我有一张正在尝试从中生成报告的表格。 Its basically a log of when something breaks (goes down), and then gets fixed. 它基本上是什么时候发生故障(崩溃)然后修复的日志。

Table schema and some example data is below. 表架构和一些示例数据如下。 To illustrate: 为了显示:
1 row is inserted when it goes down 下降时插入1行
1 row is inserted when it comes back up. 备份时插入1行。

What I am trying to do is report on various aspects, things like: 我想做的是报告各个方面,例如:
Amount of downtime in a given day / week / month 给定日期/星期/月的停机时间
Number of times its gone down in a given day / week / month. 在给定的日期/星期/月中发生故障的次数。

Ideally in a way that would easily be exported to excel or something similar to be graphed. 理想情况下,可以轻松导出为ex​​cel或类似图形的方式。

I'm having trouble coming up with any kind of queries to get this info. 我无法提出任何查询来获取此信息。

I have this one for example: 我有一个这样的例子:

SELECT [Name], datepart(day,[Inserted]), count([SystemDown])     
FROM [DownTimeLog]
WHERE [SystemDown]=1
GROUP BY [Name],datepart(day,[Inserted]) 

which gives me the number of times the system has gone down per day, which is a good starting point. 这给了我每天系统出现故障的次数,这是一个很好的起点。

But i'm trying to come up with a way of showing the total time its been down, but I am drawing a blank. 但是我试图提出一种显示总时间下降的方法,但我正在空白。 Some days for example may be 0, sometimes it may go down a few times, so trying to sum the time difference between 2 corresponding rows is provinh tough. 例如,有些日子可能是0,有时可能会下降几次,因此尝试求和2个对应行之间的时间差是很困难的。

CREATE TABLE [dbo].[DownTimeLog]
(
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Name] VARCHAR(30) NOT NULL,
    [SystemDown] BIT NOT NULL,
    [Inserted] DATETIME NOT NULL
)

INSERT INTO [DownTimeLog] ([Name],[SystemDown],[Inserted])VALUES('System1',1,'Jun 14 2011  1:49:58:000PM')
INSERT INTO [DownTimeLog] ([Name],[SystemDown],[Inserted])VALUES('System1',0,'Jun 14 2011  2:49:58:000PM')
INSERT INTO [DownTimeLog] ([Name],[SystemDown],[Inserted])VALUES('System1',1,'Jun 15 2011  1:00:00:000PM')
INSERT INTO [DownTimeLog] ([Name],[SystemDown],[Inserted])VALUES('System1',0,'Jun 15 2011  2:00:00:000PM')
INSERT INTO [DownTimeLog] ([Name],[SystemDown],[Inserted])VALUES('System1',1,'Jun 15 2011  4:00:00:000PM')
INSERT INTO [DownTimeLog] ([Name],[SystemDown],[Inserted])VALUES('System1',0,'Jun 15 2011  5:00:00:000PM')
INSERT INTO [DownTimeLog] ([Name],[SystemDown],[Inserted])VALUES('System1',1,'Jun 17 2011  1:00:00:000PM')
INSERT INTO [DownTimeLog] ([Name],[SystemDown],[Inserted])VALUES('System1',0,'Jun 17 2011  3:00:00:000PM')
INSERT INTO [DownTimeLog] ([Name],[SystemDown],[Inserted])VALUES('System1',1,'Jun 18 2011 10:00:00:000AM')
INSERT INTO [DownTimeLog] ([Name],[SystemDown],[Inserted])VALUES('System1',0,'Jun 18 2011 11:00:00:000AM')
INSERT INTO [DownTimeLog] ([Name],[SystemDown],[Inserted])VALUES('System1',1,'Jun 18 2011  1:00:00:000PM')
INSERT INTO [DownTimeLog] ([Name],[SystemDown],[Inserted])VALUES('System1',0,'Jun 18 2011  3:30:00:000PM')
INSERT INTO [DownTimeLog] ([Name],[SystemDown],[Inserted])VALUES('System1',1,'Jun 18 2011  4:00:00:000PM')
INSERT INTO [DownTimeLog] ([Name],[SystemDown],[Inserted])VALUES('System1',0,'Jun 18 2011  8:00:00:000PM')

So for example using the data above I'd like to pull data back that was something like: 因此,例如,使用上面的数据,我想将数据拉回去,就像这样:

System1 | 系统1 | June 14 2011 | 2011年6月14日| 1 hour | 1小时 1 occurence 1次发生
System1 | 系统1 | June 15 2011 | 2011年6月15日| 2 hours | 2小时 2 occurence's 2次发生
System1 | 系统1 | June 16 2011 | 2011年6月16日| 0 hours | 0小时| 0 occurence's 0次发生
System1 | 系统1 | June 17 2011 | 2011年6月17日| 2 hours | 2小时 1 occurence 1次发生
System1 | 系统1 | June 18 2011 | 2011年6月18日| 7.5 hours | 7.5小时| 3 occurence's 3次发生

I hope someone can give me a method of doing what I am trying to do. 我希望有人可以给我一种方法来做我想做的事情。

-- Edit: -编辑:

Thanks all for some great answers. 谢谢大家的出色回答。 Helped me out a tonne. 帮了我一吨。 I thought my sql was pretty string, never heard of a cross apply though - guess i need to go back to school! 我以为我的sql很漂亮,虽然从来没有听说过交叉应用-猜猜我需要回到学校!

Cheers! 干杯!

I think if you break these into two pseudo-tables you will be able to do more. 我认为,如果将它们分成两个伪表,您将可以做更多的事情。 Try this: 尝试这个:

SELECT
    *
FROM
    ( SELECT
        *
      FROM
        [DownTimeLog] AS down
      WHERE
        [SystemDown] = 1
    ) down
    INNER JOIN ( SELECT
                    *
                 FROM
                    [DownTimeLog]
                 WHERE
                    SystemDown = 0
               ) up
    ON down.id = ( up.id - 1 ) and down.name = up.name
    ORDER BY down.[Inserted], up.inserted

You can then do all kinds of calculations on down records vs. the subsequent up records. 然后,您可以对down记录和后续up记录进行各种计算。

EDIT: Of course and @Dems points out this assumes IDs are sequential. 编辑:当然,@ Dems指出这是假定ID是连续的。 If you would rather use dates as the filter do this: 如果您希望使用日期作为过滤器,请执行以下操作:

SELECT
    *
FROM
    ( SELECT
        *
      FROM
        [DownTimeLog] AS down
      WHERE
        [SystemDown] = 1
    ) down
    CROSS APPLY ( SELECT TOP 1
                    *
                 FROM
                    [DownTimeLog] up
                 WHERE
                    SystemDown = 0

                    AND up.INSERTED > down.INSERTED
                    AND up.NAME = down.name
                    ORDER BY up.inserted 
               ) up
    ORDER BY down.[Inserted], up.inserted

Here is a more detailed example of what can be done: 这是可以做什么的更详细的示例:

SELECT
        [Down_Id]
    ,   [Down_Name]
    ,   [Down_SystemDown]
    ,   [Down_Inserted]
    ,   [Up_Id]
    ,   [Up_Name]
    ,   [Up_SystemDown]
    ,   [Up_Inserted]
    ,   CAST(DATEDIFF(mi,Down_Inserted,Up_Inserted) AS DECIMAL)/60  AS Hours_Down
    ,   DATEDIFF(mi,Down_Inserted,Up_Inserted)  AS Minutes_Down
FROM
    ( SELECT
        [ID] AS Down_Id
      , [Name] AS Down_Name
      , [SystemDown] AS Down_SystemDown
      , [Inserted] AS Down_Inserted
      FROM
        [DownTimeLog] AS down
      WHERE
        [SystemDown] = 1
    ) down
    CROSS APPLY ( SELECT TOP 1
                    [ID] AS Up_Id
                  , [Name] AS Up_Name
                  , [SystemDown] AS Up_SystemDown
                  , [Inserted] AS Up_Inserted
                  FROM
                    [DownTimeLog] up
                  WHERE
                    SystemDown = 0
                    AND up.[Inserted] > down.Down_Inserted
                    AND up.NAME = down.Down_name
                  ORDER BY
                    up.Inserted
                ) up
ORDER BY
    down.[Down_Inserted]
,   up.up_inserted

Something like this might work for you. 这样的事情可能适合您。

select D1.Name,
       dateadd(d, datediff(d, 0, D1.Inserted), 0) as [Date],
       sum(datediff(mi, D1.Inserted, D2.Inserted)) as DownTime,
       count(*) as Occurrences
from DownTimeLog as D1
  cross apply ( select top 1 Name,
                             Inserted
                from DownTimeLog
                where Name = D1.Name and
                      Inserted > D1.Inserted 
                order by Inserted             
              ) as D2
where D1.SystemDown = 1
group by D1.Name, dateadd(d, datediff(d, 0, D1.Inserted), 0)

Result: 结果:

Name                           Date                    DownTime    Occurrences
------------------------------ ----------------------- ----------- -----------
System1                        2011-06-14 00:00:00.000 60          1
System1                        2011-06-15 00:00:00.000 120         2
System1                        2011-06-17 00:00:00.000 120         1
System1                        2011-06-18 00:00:00.000 450         3

You won't get a row for days with 0 occurrences. 您不会连续几天出现0次。 And if you have downtime over midnight all time will be counted on the day of the down event. 如果您在午夜有停机时间,则所有时间都将计入停机事件当天。 DownTime is in minutes. 停机时间以分钟为单位。

WITH
  enhanced_log
AS
(
SELECT
  [start].Name                                              AS [Name],
  DATEADD(DAY, DATEDIFF(DAY, 0, [start].Inserted), 0)       AS [Date],
  [start].Inserted                                          AS [Start],
  [finish].Inserted                                         AS [Finish]
FROM
  DownTimeLog  AS [start]
OUTER APPLY
  (SELECT TOP 1 * FROM DownTimeLog WHERE name = [start].name AND Inserted > [start].Inserted ORDER BY Inserted ASC) AS [finish]
WHERE
  [start].SystemDown = 1

UNION ALL

SELECT
  [start].Name,
  [Calendar].date,
  [Calendar].date,
  [finish].Inserted
FROM
  calendar
CROSS JOIN
  system
CROSS APPLY
  (SELECT TOP 1 * FROM DownTimeLog WHERE name = [system].name AND Inserted < [calendar].date ORDER BY Inserted ASC) AS [start]
OUTER APPLY
  (SELECT TOP 1 * FROM DownTimeLog WHERE name = [system].name AND Inserted > [start].Inserted ORDER BY Inserted ASC) AS [finish]
WHERE
  [start].SystemDown = 1
)

SELECT
  Name,
  Date,
  SUM(DATEDIFF(MINUTE, Start, CASE WHEN Finish < Date + 1 THEN Finish ELSE Date + 1 END)) AS Duration,
  COUNT(*) AS Instances
FROM
  enhanced_log
WHERE
  Start <> CASE WHEN Finish < Date + 1 THEN Finish ELSE Date + 1 END
GROUP BY
  Name,
  Date

The following will produce your exact output including missing days. 以下将产生您的确切输出,包括缺少的日期。 Note, this code is based on Mikael Eriksson's answer. 请注意,此代码基于Mikael Eriksson的答案。

CREATE TABLE #DownTimeLog
(
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Name] VARCHAR(30) NOT NULL,
    [SystemDown] BIT NOT NULL,
    [Inserted] DATETIME NOT NULL
)

INSERT INTO #DownTimeLog ([Name],[SystemDown],[Inserted])VALUES('System1',1,'Jun 14 2011  1:49:58:000PM')
INSERT INTO #DownTimeLog ([Name],[SystemDown],[Inserted])VALUES('System1',0,'Jun 14 2011  2:49:58:000PM')
INSERT INTO #DownTimeLog ([Name],[SystemDown],[Inserted])VALUES('System1',1,'Jun 15 2011  1:00:00:000PM')
INSERT INTO #DownTimeLog ([Name],[SystemDown],[Inserted])VALUES('System1',0,'Jun 15 2011  2:00:00:000PM')
INSERT INTO #DownTimeLog ([Name],[SystemDown],[Inserted])VALUES('System1',1,'Jun 15 2011  4:00:00:000PM')
INSERT INTO #DownTimeLog ([Name],[SystemDown],[Inserted])VALUES('System1',0,'Jun 15 2011  5:00:00:000PM')
INSERT INTO #DownTimeLog ([Name],[SystemDown],[Inserted])VALUES('System1',1,'Jun 17 2011  1:00:00:000PM')
INSERT INTO #DownTimeLog ([Name],[SystemDown],[Inserted])VALUES('System1',0,'Jun 17 2011  3:00:00:000PM')
INSERT INTO #DownTimeLog ([Name],[SystemDown],[Inserted])VALUES('System1',1,'Jun 18 2011 10:00:00:000AM')
INSERT INTO #DownTimeLog ([Name],[SystemDown],[Inserted])VALUES('System1',0,'Jun 18 2011 11:00:00:000AM')
INSERT INTO #DownTimeLog ([Name],[SystemDown],[Inserted])VALUES('System1',1,'Jun 18 2011  1:00:00:000PM')
INSERT INTO #DownTimeLog ([Name],[SystemDown],[Inserted])VALUES('System1',0,'Jun 18 2011  3:30:00:000PM')
INSERT INTO #DownTimeLog ([Name],[SystemDown],[Inserted])VALUES('System1',1,'Jun 18 2011  4:00:00:000PM')
INSERT INTO #DownTimeLog ([Name],[SystemDown],[Inserted])VALUES('System1',0,'Jun 18 2011  8:00:00:000PM')

CREATE TABLE #DownTimeLogModified (Name nvarchar(512), [Date] nvarchar(512), DownTime int,  Occurrences int)

INSERT INTO #DownTimeLogModified (Name, [Date], DownTime,  Occurrences)
select D1.Name,
       CONVERT(VARCHAR(12), dateadd(d, datediff(d, 0, D1.Inserted), 0), 107) as [Date],
       sum(datediff(mi, D1.Inserted, D2.Inserted)) as DownTime,
       count(*) as Occurrences
from #DownTimeLog as D1
  cross apply ( select top 1 Name,
                             Inserted
                from #DownTimeLog
                where SystemDown = 0 and
                      Name = D1.Name and
                      Inserted > D1.Inserted 
                order by Inserted             
              ) as D2
where D1.SystemDown = 1
group by D1.Name, dateadd(d, datediff(d, 0, D1.Inserted), 0)

DECLARE @startdate datetime,@enddate datetime
select @startdate = MIN(Inserted) FROM #DownTimeLog
select @enddate = MAX(Inserted) FROM #DownTimeLog

;WITH DateIntervalsCTE AS
        (
        SELECT 1 i,@startdate AS Date
        UNION ALL
        SELECT i + 1, DATEADD(day, i, @startdate )
        FROM DateIntervalsCTE 
        WHERE DATEADD(day, i, @startdate ) <= @enddate
        )

SELECT CASE WHEN a.Name is null THEN 'No System downtime' ELSE a.Name END as Name,CONVERT(VARCHAR(12), b.Date, 107) AS Date,CASE WHEN a.DownTime is null THEN 0 ELSE a.DownTime END AS DownTime,CASE WHEN a.Occurrences is null THEN 0 ELSE a.Occurrences END AS Occurrences 
FROM DateIntervalsCTE b
LEFT JOIN #DownTimeLogModified a ON a.Date = CONVERT(VARCHAR(12), b.Date, 107)

DROP TABLE #DownTimeLog
DROP TABLE #DownTimeLogModified

Output 产量

Name                           Date                    DownTime    Occurrences
------------------------------ ----------------------- ----------- -----------
System1                        Jun 14, 2011                60           1
System1                        Jun 15, 2011               120           2
No system downtime             Jun 16, 2011                 0           0
System1                        Jun 17, 2011               120           1
System1                        Jun 18, 2011               450           3

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM