Это похоже на проблему пропусков и островов.
Вот один из способов сделать это.Скорее всего, он будет работать быстрее, чем ваш вариант.
Стандартная идея для пробелов и островков - создать два набора номеров строк, разделив их двумя способами.Разница между такими номерами строк (rn1-rn2
) останется неизменной в каждом последующем фрагменте.Запустите запрос под CTE-за-CTE и просмотрите промежуточные результаты, чтобы увидеть, что происходит.
WITH
CTE_RN
AS
(
SELECT
[ValueId]
,[ListId]
,[ValueDelta]
,[ValueCreated]
,ROW_NUMBER() OVER (PARTITION BY ListID ORDER BY ValueCreated) AS rn1
,ROW_NUMBER() OVER (PARTITION BY ListID, [ValueDelta] ORDER BY ValueCreated) AS rn2
FROM [Value]
)
SELECT
ListID
,MIN(ValueID) AS FirstID
,MAX(ValueID) AS LastID
,MIN(ValueCreated) AS FirstCreated
,MAX(ValueCreated) AS LastCreated
,ValueDelta
,COUNT(*) AS ValueCount
FROM CTE_RN
GROUP BY
ListID
,ValueDelta
,rn1-rn2
ORDER BY
FirstCreated
;
Этот запрос дает тот же результат, что и ваш в вашем наборе данных.
Этоне совсем ясно, могут ли FirstID
и LastID
быть MIN
и MAX
, или они действительно должны быть из первой и последней строк (если они упорядочены ValueCreated).Если вам действительно нужны первое и последнее, запрос станет немного сложнее.
В исходных данных выборки «first» и «min» для FirstID
одинаковы.Давайте немного изменим примерный набор данных, чтобы подчеркнуть это различие:
insert into [Value]
([ListId], [ValueDelta], [ValueCreated])
values
(1, 1, '2019-01-01 01:01:02'), -- 1.1
(1, 0, '2019-01-01 01:02:01'), -- 2.1
(1, 0, '2019-01-01 01:03:01'), -- 2.2
(1, 0, '2019-01-01 01:04:01'), -- 2.3
(1, -1, '2019-01-01 01:05:01'), -- 3.1
(1, -1, '2019-01-01 01:06:01'), -- 3.2
(1, 1, '2019-01-01 01:01:01'), -- 1.2
(1, 1, '2019-01-01 01:08:01'), -- 4.2
(2, 1, '2019-01-01 01:08:01') -- 5.1
;
Все, что я сделал, это поменял ValueCreated между первой и седьмой строками, поэтому теперь FirstID
первой группы равен 7
и LastID
равно 1
.Ваш запрос возвращает правильный результат.Мой простой запрос выше не делает.
Вот вариант, который дает правильный результат.Я решил использовать функции FIRST_VALUE
и LAST_VALUE
, чтобы получить соответствующие идентификаторы.Опять же, запустите запрос CTE-by-CTE и изучите промежуточные результаты, чтобы увидеть, что происходит.Этот вариант дает тот же результат, что и ваш запрос, даже с настроенным набором данных выборки.
WITH
CTE_RN
AS
(
SELECT
[ValueId]
,[ListId]
,[ValueDelta]
,[ValueCreated]
,ROW_NUMBER() OVER (PARTITION BY ListID ORDER BY ValueCreated) AS rn1
,ROW_NUMBER() OVER (PARTITION BY ListID, ValueDelta ORDER BY ValueCreated) AS rn2
FROM [Value]
)
,CTE2
AS
(
SELECT
ValueId
,ListId
,ValueDelta
,ValueCreated
,rn1
,rn2
,rn1-rn2 AS Diff
,FIRST_VALUE(ValueID) OVER(
PARTITION BY ListID, ValueDelta, rn1-rn2 ORDER BY ValueCreated
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS FirstID
,LAST_VALUE(ValueID) OVER(
PARTITION BY ListID, ValueDelta, rn1-rn2 ORDER BY ValueCreated
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS LastID
FROM CTE_RN
)
SELECT
ListID
,FirstID
,LastID
,MIN(ValueCreated) AS FirstCreated
,MAX(ValueCreated) AS LastCreated
,ValueDelta
,COUNT(*) AS ValueCount
FROM CTE2
GROUP BY
ListID
,ValueDelta
,rn1-rn2
,FirstID
,LastID
ORDER BY FirstCreated;