在组中提取开始和结束日期

时间:2016-03-15 13:22:56

标签: sql sql-server

我有一个SQL Server表,每行代表一个机器日志,说明机器开机或关机的时间。列是ACTION,MACHINE_NAME,TIME_STAMP

ACTION是一个可以“开启”或“关闭”的字符串 MACHINE_NAME是表示计算机ID的String TIME_STAMP是一个日期。

一个例子:

ACTION        MACHINE_NAME          TIME_STAMP
ON            PC1                   2016/03/04 17:13:10
OFF           PC1                   2016/03/04 17:13:15 
ON            PC1                   2016/03/04 17:14:15 
OFF           PC1                   2016/03/04 17:15:45 

我需要从这些日志中提取一个新表格,告诉我:“从START_TIME到END_TIME,机器X已经开启了N分钟”

如何编写SQL查询才能执行此操作?

期望的结果

MACHINE_NAME          START_TIME                    END_TIME
PC1                   2016/03/04 17:13:10           2016/03/04 17:13:15
PC1                   2016/03/04 17:14:15           2016/03/04 17:15:45               

4 个答案:

答案 0 :(得分:2)

我能够使用CTE和LAG函数解决这个问题。这样,我们就可以为每个'ON'获取第一个'OFF'操作,然后应用ROW_NUMBER来匹配它们:

;WITH first_ON AS 
(
    SELECT *, LAG(m.ACTION, 1, 'OFF') OVER (PARTITION BY m.MACHINE_NAME ORDER BY m.TIME_STAMP) AS previous_action
    FROM your_table m
), 
ON_actions AS
(
    SELECT
            m.ACTION,
            m.MACHINE_NAME,
            m.TIME_STAMP,
            ROW_NUMBER() OVER ( ORDER BY TIME_STAMP ) AS RN
    FROM first_ON m
    WHERE m.previous_action = 'OFF' AND m.ACTION = 'ON'
), 
OFF_actions AS (
    SELECT 
            m.ACTION,
            m.MACHINE_NAME,
            m.TIME_STAMP,
            ROW_NUMBER() OVER ( ORDER BY TIME_STAMP ) AS RN
    FROM your_table m
    WHERE m.ACTION = 'OFF'
)

SELECT a.MACHINE_NAME, a.TIME_STAMP AS START_TIME, b.TIME_STAMP AS END_TIME
FROM ON_actions a
INNER JOIN OFF_actions b ON a.MACHINE_NAME = b.MACHINE_NAME AND a.RN = b.RN

编辑:此解决方案还考虑了无与伦比的ON和OFF,例如ON,ON,OFF,ON,ON,ON,OFF,OFF。

答案 1 :(得分:1)

您可以使用如下相关查询来执行此操作:

SELECT t.Machine_name,
       t.time_stamp as start_date,
       (SELECT min(s.time_stamp) from YourTable s
        WHERE t.Machine_name = s.Machine_Name
          and s.ACTION = 'OFF'
          and s.time_stamp > t.time_stamp) as end_date
FROM YourTable t
WHERE t.action = 'ON'

编辑:

SELECT * FROM (
    SELECT t.Machine_name,
           t.time_stamp as start_date,
           (SELECT min(s.time_stamp) from YourTable s
            WHERE t.Machine_name = s.Machine_Name
              and s.ACTION = 'OFF'
              and s.time_stamp > t.time_stamp) as end_date
    FROM YourTable t
    WHERE t.action = 'ON')
WHERE end_date is not null

答案 2 :(得分:0)

根据time_stampmachine_name以及group_id提供row_number。

<强>查询

;with cte as(
    select rn = row_number() over(      
        order by time_stamp, machine_name
    ), *
    from your_table_name
)
select z.machine_name, z.start_time,
case when z.start_time = z.end_time then null 
    else z.end_time end as end_time from(
        select t.machine_name, min(t.time_stamp) as start_time, 
        max(t.time_stamp) as end_time from(
            select ((rn - 1) / 2) + 1 AS Group_Id, * 
            from cte
     )t
     group by t.machine_name, t.Group_Id
)z;

答案 3 :(得分:0)

将表连接到自身的内部将产生所需的输出,并且在SQL Server中编写起来相当简单。我经常在工作的地方使用这种格式。

SELECT m1.MACHINE_NAME, m1.TIME_STAMP AS START_TIME, MIN(m2.TIME_STAMP) AS END_TIME
FROM MACHINE_LOG m1
INNER JOIN MACHINE_LOG m2 ON m1.MACHINE_NAME = m2.MACHINE_NAME
    AND m2.ACTION = 'OFF'
    AND m2.TIME_STAMP > m1.TIME_STAMP
WHERE m1.ACTION = 'ON'
GROUP BY m1.MACHINE_NAME, m1.START_TIME