我有一个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
答案 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_stamp
和machine_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