基于来自多行SQL Server的标志的时间总和

时间:2011-11-08 05:37:57

标签: sql sql-server

我很难执行其中一个查询。我需要根据同一张桌子上的标志找到点火时间。表格如下,

UnitId      eventtime               ign
----------- ----------------------- -----
356         2011-05-04 10:41:00.000 1
356         2011-05-04 10:42:00.000 1
356         2011-05-04 10:43:00.000 1
356         2011-05-04 10:45:00.000 1
356         2011-05-04 10:47:00.000 1
356         2011-05-04 10:48:00.000 0
356         2011-05-04 11:14:00.000 1
356         2011-05-04 11:14:00.000 1
356         2011-05-04 11:15:00.000 1
356         2011-05-04 11:15:00.000 1
356         2011-05-04 11:15:00.000 1
356         2011-05-04 11:16:00.000 0
356         2011-05-04 11:16:00.000 0
356         2011-05-04 11:16:00.000 0
356         2011-05-04 14:49:00.000 1
356         2011-05-04 14:50:00.000 1
356         2011-05-04 14:50:00.000 1
356         2011-05-04 14:51:00.000 1
356         2011-05-04 14:52:00.000 0
356         2011-05-04 14:52:00.000 0
356         2011-05-04 20:52:00.000 0

此处,Ign标志将确定ignition_on和iginition_off时间。 所以在上面我们可以有点火对

2011-05-04 10:41:00.000 - 2011-05-04 10:48:00.000
2011-05-04 11:14:00.000 - 2011-05-04 11:16:00.000
2011-05-04 14:49:00.000 - 2011-05-04 14:52:00.000

所以从上面的一对我可以说我的单位运行7 + 2 + 3 = 12分钟。我不想在我的结果中完成上面的对,仅作为例子。我的目标是获得12分钟的结果。

我如何使用单个查询来实现它,现在我正在使用CURSOR循环,但是这个东西需要花费更多的时间用于多天和多个单元。无论如何,如果没有CURSOR,我能做到吗?

3 个答案:

答案 0 :(得分:2)

现在已经很晚了,所以这次不是写出所有的代码,而是要总结一下如何做到这一点:

将表连接到其自身,其中右侧的一个连接条件是相关子查询,其从表>表中选择前1分钟(事件时间)。从左侧开始的当前行。然后使用where子句将其过滤到左侧的ign列为<>的行。到右边的ign列。这会让你得到你的改变。使用类似的技术第三次连接回表格,你将拥有既有开始时间又有结束时间的记录,可以使用日期函数来获取分钟值。

答案 1 :(得分:1)

如果我们有一个额外的标准来区分具有相同ign值的相邻事件序列,我们可以从每个序列中获取ign=1最早的事件并将其与最早的事件联系起来相应ign=0序列的事件。

可以添加这样的标准,如下所示。我将首先发布解决方案,然后解释它是如何工作的。

首先,设置:

DECLARE @atable TABLE (
  Id int IDENTITY,
  UnitId int,
  eventtime datetime,
  ign bit
);
INSERT INTO @atable (UnitId, eventtime, ign)
SELECT 356, '2011-05-04 10:41:00.000', 1 UNION ALL
SELECT 356, '2011-05-04 10:42:00.000', 1 UNION ALL
SELECT 356, '2011-05-04 10:43:00.000', 1 UNION ALL
SELECT 356, '2011-05-04 10:45:00.000', 1 UNION ALL
SELECT 356, '2011-05-04 10:47:00.000', 1 UNION ALL
SELECT 356, '2011-05-04 10:48:00.000', 0 UNION ALL
SELECT 356, '2011-05-04 11:14:00.000', 1 UNION ALL
SELECT 356, '2011-05-04 11:14:00.000', 1 UNION ALL
SELECT 356, '2011-05-04 11:15:00.000', 1 UNION ALL
SELECT 356, '2011-05-04 11:15:00.000', 1 UNION ALL
SELECT 356, '2011-05-04 11:15:00.000', 1 UNION ALL
SELECT 356, '2011-05-04 11:16:00.000', 0 UNION ALL
SELECT 356, '2011-05-04 11:16:00.000', 0 UNION ALL
SELECT 356, '2011-05-04 11:16:00.000', 0 UNION ALL
SELECT 356, '2011-05-04 14:49:00.000', 1 UNION ALL
SELECT 356, '2011-05-04 14:50:00.000', 1 UNION ALL
SELECT 356, '2011-05-04 14:50:00.000', 1 UNION ALL
SELECT 356, '2011-05-04 14:51:00.000', 1 UNION ALL
SELECT 356, '2011-05-04 14:52:00.000', 0 UNION ALL
SELECT 356, '2011-05-04 14:52:00.000', 0 UNION ALL
SELECT 356, '2011-05-04 20:52:00.000', 0;

现在查询:

WITH
marked AS (
  SELECT
    *,
    Grp = ROW_NUMBER() OVER (PARTITION BY UnitId ORDER BY eventtime) -
     ROW_NUMBER() OVER (PARTITION BY UnitId, ign ORDER BY eventtime)
  FROM @atable
),
ranked AS (
  SELECT
    *,
    seqRank = DENSE_RANK() OVER (PARTITION BY UnitId, ign ORDER BY Grp),
    eventRank = ROW_NUMBER() OVER (PARTITION BY UnitId, ign, Grp ORDER BY eventtime)
  FROM marked
),
final AS (
  SELECT
    s.UnitId,
    EventStart = s.eventtime,
    EventEnd   = e.eventtime
  FROM ranked s
    INNER JOIN ranked e ON s.UnitId = e.UnitId AND s.seqRank = e.seqRank
  WHERE s.ign = 1
    AND e.ign = 0
    AND s.eventRank = 1
    AND e.eventRank = 1
)
SELECT *
FROM final
ORDER BY
  UnitId,
  EventStart

这是它的工作原理。

marked公用表表达式(CTE)为我们提供了我在开始时讨论的附加标准。它产生的结果集如下所示:

Id  UnitId  eventtime                ign  Grp
--  ------  -----------------------  ---  ---
1   356     2011-05-04 10:41:00.000  1    0
2   356     2011-05-04 10:42:00.000  1    0
3   356     2011-05-04 10:43:00.000  1    0
4   356     2011-05-04 10:45:00.000  1    0
5   356     2011-05-04 10:47:00.000  1    0
6   356     2011-05-04 10:48:00.000  0    5
7   356     2011-05-04 11:14:00.000  1    1
8   356     2011-05-04 11:14:00.000  1    1
9   356     2011-05-04 11:15:00.000  1    1
10  356     2011-05-04 11:15:00.000  1    1
11  356     2011-05-04 11:15:00.000  1    1
12  356     2011-05-04 11:16:00.000  0    10
13  356     2011-05-04 11:16:00.000  0    10
14  356     2011-05-04 11:16:00.000  0    10
15  356     2011-05-04 14:49:00.000  1    4
16  356     2011-05-04 14:50:00.000  1    4
17  356     2011-05-04 14:50:00.000  1    4
18  356     2011-05-04 14:51:00.000  1    4
19  356     2011-05-04 14:52:00.000  0    14
20  356     2011-05-04 14:52:00.000  0    14
21  356     2011-05-04 20:52:00.000  0    14

您可以自己查看具有相同ign的每个事件序列现在如何通过其自己的(UnitId, ign, Grp)键轻松区分。所以现在我们可以对每个序列以及序列中的每个事件进行排序,这就是ranked CTE的作用。它产生以下结果集:

Id  UnitId  eventtime                ign  Grp  seqRank  eventRank
--  ------  -----------------------  ---  ---  -------  ---------
1   356     2011-05-04 10:41:00.000  1    0    1        1
2   356     2011-05-04 10:42:00.000  1    0    1        2
3   356     2011-05-04 10:43:00.000  1    0    1        3
4   356     2011-05-04 10:45:00.000  1    0    1        4
5   356     2011-05-04 10:47:00.000  1    0    1        5
6   356     2011-05-04 10:48:00.000  0    5    1        1
7   356     2011-05-04 11:14:00.000  1    1    2        1
8   356     2011-05-04 11:14:00.000  1    1    2        2
9   356     2011-05-04 11:15:00.000  1    1    2        3
10  356     2011-05-04 11:15:00.000  1    1    2        4
11  356     2011-05-04 11:15:00.000  1    1    2        5
12  356     2011-05-04 11:16:00.000  0    10   2        1
13  356     2011-05-04 11:16:00.000  0    10   2        2
14  356     2011-05-04 11:16:00.000  0    10   2        3
15  356     2011-05-04 14:49:00.000  1    4    3        1
16  356     2011-05-04 14:50:00.000  1    4    3        2
17  356     2011-05-04 14:50:00.000  1    4    3        3
18  356     2011-05-04 14:51:00.000  1    4    3        4
19  356     2011-05-04 14:52:00.000  0    14   3        1
20  356     2011-05-04 14:52:00.000  0    14   3        2
21  356     2011-05-04 20:52:00.000  0    14   3        3

您可以看到ign=1序列现在可以在ign=0的帮助下与seqRank序列匹配。并且只选择每个序列中最早的事件(按eventRank=1过滤),我们将获得所有ign=1序列的开始和结束时间。因此final CTE的结果是:

UnitId  EventStart               EventEnd
------  -----------------------  -----------------------
356     2011-05-04 10:41:00.000  2011-05-04 10:48:00.000
356     2011-05-04 11:14:00.000  2011-05-04 11:16:00.000
356     2011-05-04 14:49:00.000  2011-05-04 14:52:00.000

显然,如果最后一个ign=1序列后面没有ign=0事件,则不会在最终结果中显示,因为最后ign=1序列没有匹配ign=0序列,使用上述方法。

有一种可能的情况,此查询无法正常工作。当事件列表以ign=0事件而不是ign=1开头时。如果这实际上是可行的,您只需将以下过滤器添加到ranked CTE:

WHERE NOT (ign = 0 AND Grp = 0)
-- Alternatively: WHERE ign <> 0 OR Grp <> 0

利用Grp的第一个值始终为0的事实。因此,如果将0分配给ign=0的事件,则应排除这些事件。


有用的阅读:

答案 2 :(得分:0)

您可以根据row_number()

加入自己的表格
select t1.RowNo as t1Row, t2.RowNo as t2Row, t1.StarTime, t2.EndTime as EndTime, cast(t2.EndTime - t1.StarTime as time) as Duration, t1.ign as IgnPeriod
from (select eventtime as StarTime, ign, ROW_NUMBER() over (order by eventtime) as RowNo from UnitTable)  as t1
inner join (select * from (select eventtime as EndTime, ROW_NUMBER() over (order by eventtime) as RowNo from UnitTable) as t_inner) t2 on t1.RowNo + 1 = t2.RowNo

然后,您可以将ign = 1个句点的持续时间相加:

select cast(dateadd(millisecond,sum(datediff(millisecond,0,duration)),0) as time) from 
(select cast(t2.EndTime - t1.StarTime as time) as Duration, t1.ign as IgnPeriod
from (select eventtime as StarTime, ign, ROW_NUMBER() over (order by eventtime) as RowNo from UnitTable)  as t1
inner join (select * from (select eventtime as EndTime, ROW_NUMBER() over (order by eventtime) as RowNo from UnitTable) as t_inner) t2 on t1.RowNo + 1 = t2.RowNo
) t_outer
where t_outer.IgnPeriod = 1