使用大型数据集计算机器在不同状态下花费的时间的有效方法

Efficient way of calculating time spent by machines in different states with a large dataset

我找不到解决这个问题的好方法。我有一个看起来像这样的数据集:

MACHINE     DATETIME                 CODE  
C54118PC1   2016-04-01 00:00:01.000  10
C54118PC1   2016-04-01 00:01:12.000  4
C54118PC1   2016-04-01 00:01:36.000  10
C54118PC1   2016-04-01 00:01:50.000  4
C54123PC1   2016-04-01 00:00:02.000  0
C54123PC1   2016-04-01 01:00:02.000  0

最终,我想在 Qlikview 文档中将其显示为饼图,显示机器在每个状态(代码)中花费的时间。

为了做到这一点,我想我需要以这种格式获取数据:

MACHINE     DATE       CODE DURATION
Machine1    07/06/2016  2   07:30:14
Machine1    07/06/2016  5   02:45:10
Machine2    07/06/2016  0   12:37:05
Machine2    07/06/2016  5   04:04:59
Machine1    08/06/2016  2   02:57:13
Machine1    08/06/2016  4   04:12:05
Machine1    08/06/2016  5   03:07:12
Machine3    08/06/2016  8   02:49:23

这将是每台机器每天每个代码的 1 行,以及当天和代码的总持续时间。

另一个考虑因素是这是一个大型数据集 - 目前至少有 200 万行,并且随着更多机器的添加和更多时间的流逝,这可能会增长得相当快。

所以,基本上我需要一种非常有效的方法来计算在不同状态(代码)下花费的时间,按机器和日期。我认为这最好在 SQL 中完成,但如果这样做效率更高,我也可以在 Qlikview 中完成。谁能帮忙?

提前致谢!




编辑:对不起,我忘了解释日志记录是如何工作的!我们有一堆机器在白天的各种状态(例如打印、空闲、清洁)之间交替。每次他们改变状态(例如从打印到空闲),他们记录一个时间戳和他们的新状态。

(他们还定期记录时间戳和当前状态,即使他们实际上并没有改变状态。)

持续时间计算为两个连续时间戳之间的时间差。

未经测试: LEAD() 一个分析(window 函数)让您可以查看给定数据分区的下一个有序行值似乎可行...

所以在这种情况下:我们想要给定机器的下一行的日期时间,而不管状态如何......类似于:

SELECT MACHINE
     , FORMAT(DATETIME, 'DD/MM/YYYY')
     , CODE
     , SUM(Lead(DateTIme) over (partition by Machine order by dateTime asc) - datetime) AS DURATION, 
FROM TABLENAME
GROUP BY MACHINE, CODE, FORMAT(DATETIME, 'DD/MM/YYYY')

我不确定这里的数据数学如何计算持续时间。

试试这个

Declare @Table table (machine varchar(25),DateTime datetime,Code int)
Insert into @Table (Machine,DateTime,Code) values 
('Machine1','2016-04-01 00:00:01.000',10),
('Machine1','2016-04-01 00:01:12.000',4),
('Machine1','2016-04-01 00:01:36.000',10),
('Machine1','2016-04-01 00:01:50.000',4),
('Machine2','2016-04-01 00:00:02.000',0),
('Machine2','2016-04-01 01:00:02.000',0),
('Machine1','2016-04-02 00:00:01.000',10),
('Machine1','2016-04-02 00:01:12.000',4),
('Machine1','2016-04-02 00:01:36.000',10),
('Machine1','2016-04-02 00:01:50.000',4),
('Machine2','2016-04-02 00:00:02.000',0),
('Machine2','2016-04-02 01:00:02.000',0)


IF OBJECT_ID('tempdb.dbo.#Temp1', 'U') IS NOT NULL
    DROP TABLE #Temp1; 

-- Generate Date Range
Select Distinct DateR1=cast(DateTime as Date),DateR2=DateAdd(DD,1,cast(DateTime as Date)) into #Temp1 from @Table
Create Index idx on #Temp1 (DateR1,DateR2)

;with cteBase as (
        Select *
             ,MSDate1 = DateTime
             ,MSDate2 = Lead(DateTime,1,DateAdd(DD,1,cast(DateTime as Date)))  over (Partition by Machine Order by Machine,DateTime) 
         From @Table
      )
Select Machine
      ,Date = DateR1
      ,Code
      ,Duration = CONVERT(varchar, DATEADD(SS, sum(DateDiff(SS,case when MSDate1<=DateR1 then DateR1 else MSDate1 end,case when MSDate2>DateR2 then DateR2 else MSDate2 end)), 0), 114)
 From #Temp1 A
 Join cteBase B on (MSDate1 between DateR1 and DateR2 or MSDate2 between DateR1 and DateR2)
 Group By Machine
      ,DateR1
      ,Code

Returns

Machine         Date        Code    Duration
Machine1        2016-04-01  4       23:58:34:000
Machine1        2016-04-01  10      00:01:25:000
Machine1        2016-04-02  4       23:58:35:000
Machine1        2016-04-02  10      00:01:25:000
Machine2        2016-04-01  0       23:59:58:000
Machine2        2016-04-02  0       00:00:00:000

这可以在 QV 本身中轻松实现(参见下面的脚本)。

关于数据大小。您可以在 QV 中实现 Incremental Load 并在重新加载应用程序时仅加载新记录,并且仅对小集执行转换而不是对完整数据集执行转换。

示例工作流将是:在每次重新加载时仅提取最后日期的记录,执行转换,从 qvd 加载先前转换的数据并将新结果连接到 qvd。这是 QV 非常常见的方法(我每天都在使用它)。通常我更喜欢这种方法,因为我不会推动数据库来完成 QV 的工作并避免与 DBA 发生冲突;)

如果你愿意,我可以粘贴带有增量加载的示例脚本(只需留下评论)

有关增量加载的更多详细信息:

示例脚本:

RawData:
Load
    MACHINE,  
//  DATETIME, // this is not needed anymore
    CODE,
    date(left(DATETIME, 10)) as Date, // extract the date
    mid(DATETIME, 12, 8) as Duration // extract the duration
; 
Load * Inline [
    MACHINE   , DATETIME                 , CODE  
    C54118PC1 , 2016-04-01 00:00:01.000  , 10
    C54118PC1 , 2016-04-01 00:01:12.000  , 4
    C54118PC1 , 2016-04-01 00:01:36.000  , 10
    C54118PC1 , 2016-04-01 00:01:50.000  , 4
    C54123PC1 , 2016-04-01 00:00:02.000  , 0
    C54123PC1 , 2016-04-01 01:00:02.000  , 0
];

Data:
Load
    interval(sum(Duration)) as TotalDuration, // sum the total duration and convert it to timestamp format
    MACHINE,  
    Date, 
    CODE
Resident
    RawData
Group By
    MACHINE,  
    Date, 
    CODE    
;   

Drop Table RawData; // this table is not needed anymore