如果我们有一个额外的标准来区分具有相同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 序列现在可以在seqRank 的帮助下与ign=0 序列匹配。并且只从每个序列中挑选最早的事件(按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 的事件,则应排除这些事件。
有用的阅读: