【问题标题】:SQL: Reporting on data by dateSQL:按日期报告数据
【发布时间】:2011-06-20 21:06:43
【问题描述】:

我有一张表,我正试图从中生成报告。 它基本上是记录什么时候发生故障(发生故障),然后得到修复的日志。

表架构和一些示例数据如下。 举例说明:
下降时插入 1 行
恢复时插入 1 行。

我想做的是报告各个方面,例如:
给定日/周/月的停机时间
在给定的一天/一周/一个月内下降的次数。

理想情况下,可以轻松导出到 Excel 或类似的图形。

我无法提出任何类型的查询来获取此信息。

我有这个例子:

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

这给了我系统每天出现故障的次数,这是一个很好的起点。

但我试图想出一种方法来显示它的总时间,但我正在画一个空白。 例如,有些天可能是 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')

因此,例如使用上面的数据,我想拉回类似的数据:

系统1 | 2011 年 6 月 14 日 | 1 小时 | 1 次出现
系统1 | 2011 年 6 月 15 日 | 2 小时 | 2次出现的
系统1 | 2011 年 6 月 16 日 | 0 小时 | 0 次出现的
系统1 | 2011 年 6 月 17 日 | 2 小时 | 1 次出现
系统1 | 2011 年 6 月 18 日 | 7.5 小时 | 3次出现的

我希望有人能给我一个方法来做我想做的事情。

-- 编辑:

感谢大家的一些很棒的答案。帮了我一吨。 我认为我的 sql 是很漂亮的字符串,但从未听说过交叉申请 - 我想我需要回到学校!

干杯!

【问题讨论】:

  • 您使用的是什么版本的 SQL Server?像this self-join solution 这样的东西可能对你有用。
  • 如果系统从晚上 11 点到凌晨 1 点停机,是每天 1 小时,还是第一天 2 小时?
  • 如果系统在某一天完全没有宕机,还需要显示0小时的记录吗?

标签: sql sql-server tsql


【解决方案1】:

以下将产生您的确切输出,包括缺失的天数。请注意,此代码基于 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

输出

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

【讨论】:

    【解决方案2】:

    这样的事情可能对你有用。

    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)
    

    结果:

    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
    

    您不会连续几天出现 0 次。如果您在午夜有停机时间,则所有时间都将计入停机事件当天。停机时间以分钟为单位。

    【讨论】:

      【解决方案3】:

      我认为,如果您将它们分成两个伪表,您将能够做更多事情。试试这个:

      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
      

      然后,您可以对 down 记录与随后的 up 记录进行各种计算。

      编辑:当然,@Dems 指出这假设 ID 是连续的。如果您更愿意使用日期作为过滤器,请执行以下操作:

      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
      

      下面是一个更详细的例子:

      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
      

      【讨论】:

      • 您已经假设这里只有一个系统插入到表中,因此所有记录都是顺序的。 System 字段的存在导致我做出相反的假设。
      • @Dems,你说得很好。一开始我忘了加入name。此外,根据系统的实际设置方式,使用inserted 列可能比依赖顺序 ID 更好。
      【解决方案4】:
      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-12-23
        • 1970-01-01
        • 2015-11-02
        • 1970-01-01
        • 2013-02-09
        • 2021-04-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多