EDIT:
После проверки ни одна из моих оригинальных работ по этому ответу не была очень хорошей. Это на самом деле относится к классу проблем, известных как пробелы и острова , и в этом пересмотренном ответе будет использоваться информация, которую я почерпнул из аналогичных вопросов / выучил с тех пор, как впервые ответил на этот вопрос.
Оказывается, этот запрос можно сделать на лот проще, чем я думал:
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
<ч />
Так что здесь происходит? Одним из определений проблем пробелов и островков является необходимость «групп» последовательных значений (или их отсутствие). Часто последовательности создаются для решения этой проблемы, используя часто упускаемый из виду / слишком интуитивный факт: вычитание последовательностей дает постоянное значение.
Например, представьте следующие последовательности и вычитание (значения в строках не важны):
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 или меньше; он считает количество строк на группу.