编辑:
经过审查,我在这个答案上的原创作品都不是很好。这实际上属于被称为gaps-and-islands的一类问题,这个修订后的答案将使用我从第一次回答这个问题以来从类似问题中收集/学到的信息。
事实证明,这个查询比我最初想象的要简单得多:
WITH Grouped_Run AS (SELECT heartRate, dateTime,
ROW_NUMBER() OVER(ORDER BY dateTime) -
ROW_NUMBER() OVER(PARTITION BY heartRate ORDER BY dateTime) AS groupingId
FROM HeartRate)
SELECT heartRate, MIN(dateTime), MAX(dateTime)
FROM Grouped_Run
GROUP BY heartRate, groupingId
HAVING COUNT(*) > 2
SQL Fiddle Demo
那么这里发生了什么?差距和孤岛问题的定义之一是需要连续值的“组”(或缺少连续值)。通常会生成序列来解决这个问题,利用一个经常被忽视/过于直观的事实:减去序列会产生一个恒定值。
例如,想象以下序列和减法(行中的值不重要):
position positionInGroup subtraction
=========================================
1 1 0
2 2 0
3 3 0
4 1 3
5 2 3
6 1 5
7 4 3
8 5 3
position
是在所有记录上生成的简单序列。
positionInGroup
是为每组不同的记录生成的简单序列。在这种情况下,实际上有 3 组不同的记录(从 开始position = 1, 4, 6
)。
subtraction
是其他两列之间差异的结果。请注意,不同组的值可能会重复!
序列必须共享的关键属性之一是它们必须以相同的顺序在数据行上生成,否则会中断。
那么 SQL 是如何做到这一点的呢?通过使用ROW_NUMBER()
此函数,将在记录“窗口”上生成一系列数字:
ROW_NUMBER() OVER(ORDER BY dateTime)
将生成position
序列。
ROW_NUMBER() OVER(PARTITION BY heartRate ORDER BY dateTime)
将生成positionInGroup
序列,每个序列heartRate
都是不同的组。
对于这种类型的大多数查询,两个序列的值并不重要,重要的是减法(获取序列组),所以我们只需要减法的结果。
我们还需要heartRate
他们发生的时间和时间来提供答案。
最初的答案要求每个“运行”卡住心跳的开始和结束时间。这是一个标准MIN(...)
/ MAX(...)
,这意味着一个GROUP BY
. 我们需要同时使用原始列heartRate
(因为这是一个非聚合列)和我们生成的列groupingId
(它标识每个卡住值的当前“运行”)。
部分问题只要求重复三次或更多次的运行。这HAVING COUNT(*) > 2
是一条忽略长度为 2 或更短的运行的指令;它计算每组的行数。