SQL Server - эффективный способ группировки значений по первой дате, когда они произошли, прежде чем они повторятся - PullRequest
0 голосов
/ 29 апреля 2019

Я не уверен, что заголовок точно описывает то, что я хочу сделать, но вот рабочий пример, который мне нужно ускорить ...

/*
Aim:
2018-01-01, 1
2018-09-01, 2
2019-01-01, 1
2019-04-01, 3
2020-04-01, 1
2020-09-01, 2
2021-01-01, 1
*/

;WITH temp (ID, GroupID, Date, Value) AS (
    SELECT * FROM (VALUES 
        (1, 1, CAST('2018-01-01' AS DATE), 1),
        (2, 1, CAST('2018-04-01' AS DATE), 1),
        (3, 1, CAST('2018-09-01' AS DATE), 2),
        (4, 1, CAST('2019-01-01' AS DATE), 1),
        (5, 1, CAST('2019-04-01' AS DATE), 3),
        (6, 1, CAST('2019-09-01' AS DATE), 3),
        (7, 1, CAST('2020-01-01' AS DATE), 3),
        (8, 1, CAST('2020-04-01' AS DATE), 1),
        (9, 1, CAST('2020-09-01' AS DATE), 2),
        (10, 1, CAST('2021-01-01' AS DATE), 1),
        (11, 2, CAST('2018-01-01' AS DATE), 1),
        (12, 2, CAST('2018-04-01' AS DATE), 1),
        (13, 2, CAST('2018-09-01' AS DATE), 2),
        (14, 2, CAST('2019-01-01' AS DATE), 1),
        (15, 2, CAST('2019-04-01' AS DATE), 3),
        (16, 2, CAST('2019-09-01' AS DATE), 3),
        (17, 2, CAST('2020-01-01' AS DATE), 3),
        (18, 2, CAST('2020-04-01' AS DATE), 1),
        (19, 2, CAST('2020-09-01' AS DATE), 2),
        (20, 2, CAST('2021-01-01' AS DATE), 1)
    ) AS X(ID, GroupID, Date, Value)
)
select t1.* from temp t1
left join temp t2 on t1.GroupID = t2.GroupID
    and t1.Value = t2.Value
    and t2.Date < t1.Date
    and not exists (
        select * 
        from temp t3 
        where t1.groupID = t3.GroupID 
            and t3.Value != t1.Value 
            and t3.Date between t2.Date and t1.Date
    )
where t2.ID is null
order by t1.GroupID asc, t1.Date asc

Это возвращает результаты, которые я хочу, но когда я использую реальные данные, запрос действительно медленный. Кажется, это проверка НЕ ​​СУЩЕСТВУЕТ, которая занимает так много времени

Для каждого GroupID я хочу выбрать самую раннюю дату для каждого значения до тех пор, пока не произойдет следующее значение, и я уверен, что должен быть лучший / более быстрый способ сделать это, но не могу придумать один из них в минуту.

Любые предложения приветствуются

1 Ответ

1 голос
/ 29 апреля 2019

Это, кажется, пример проблемы групп и островов.В этом случае наилучшим решением будет использование lag():

select t.*
from (select t.*,
             lag(value) over (partition by groupid order by date) as prev_value
      from temp t
     ) t
where prev_value is null or prev_value <> value;

Здесь - это db <> скрипка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...