SQL - Получить количество на основе скользящего окна на уникальный идентификатор - PullRequest
0 голосов
/ 26 марта 2020

Я работаю с таблицей, в которой есть столбцы id и date. Для каждого идентификатора есть 90-дневное окно, в котором можно сделать несколько транзакций. 90-дневное окно открывается, когда сделана первая транзакция, и часы сбрасываются по истечении 90 дней. Когда новое 90-дневное окно начинается с новой транзакции, я хочу начать отсчет с начала в 1. Я хотел бы создать что-то вроде этого с двумя дополнительными столбцами (window и count) в SQL:

id      date        window  count   
name1   7/7/2019    first   1
name1   12/31/2019  second  1
name1   1/23/2020   second  2
name1   1/23/2020   second  3
name1   2/12/2020   second  4 
name1   4/1/2020    third   1
name2   6/30/2019   first   1
name2   8/14/2019   first   2 

Я думаю, что получить ранг окна можно с помощью CASE заявление и MIN(date) OVER (PARTITION BY id). Вот что я имею в виду для этого:

CASE WHEN MIN(date) OVER (PARTITION BY id) THEN 'first' 
WHEN DATEDIFF(day, date, MIN(date) OVER (PARTITION BY id)) <= 90 THEN 'first'
WHEN DATEDIFF(day, date, MIN(date) OVER (PARTITION BY id)) > 90 AND DATEDIFF(day, date, MIN(date) OVER (PARTITION BY id)) <= 180 THEN 'third'
WHEN DATEDIFF(day, date, MIN(date) OVER (PARTITION BY id)) > 180 AND DATEDIFF(day, date, MIN(date) OVER (PARTITION BY id)) <= 270 THEN 'fourth' 
ELSE NULL END

И увеличение числа в windows будет ROW_NUMBER() OVER (PARTITION BY id, window)?

Ответы [ 2 ]

1 голос
/ 26 марта 2020

GMB совершенно верно, что необходим рекурсивный CTE. Я предлагаю это в качестве альтернативной формы по двум причинам. Во-первых, потому что он использует SQL Синтаксис сервера, который, по-видимому, является базой данных, используемой в вопросе. Во-вторых, потому что он напрямую вычисляет window и count без оконных функций:

with t as (
      select t.*, row_number() over (partition by id order by date) as seqnum
      from tbl t
     ),
     cte as (
      select t.id, t.date, dateadd(day, 90, t.date) as window_end, 1 as window, 1 as count, seqnum
      from t
      where seqnum = 1
      union all
      select t.id, t.date,
             (case when t.date > cte.window_end then dateadd(day, 90, t.date)
                   else cte.window_end
              end) as window_end,
             (case when t.date > cte.window_end then window + 1 else window end) as window,
             (case when t.date > cte.window_end then 1 else cte.count + 1 end) as count,
             t.seqnum
      from cte join
           t
           on t.id = cte.id and
              t.seqnum = cte.seqnum + 1
     )
select id, date, window, count
from cte
order by 1, 2;

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

1 голос
/ 26 марта 2020

Вы не можете решить эту проблему только с оконными функциями. Необходимо выполнить итерацию по набору данных, что можно сделать с помощью рекурсивного запроса:

with 
    tab as (
        select t.*, row_number() over(partition by id order by date) rn
        from mytable t
    )
    cte as (
        select id, date, rn, date date0 from tab where rn = 1
        union all
        select t.id, t.date, t.rn, greatest(t.date, c.date + interval '90' day)
        from cte c
        inner join tab t on t.id = c.id and t.rn = c.rn + 1
    )
select
    id,
    date,
    dense_rank() over(partition by id order by date0) grp,
    count(*)     over(partition by id order by date0, date) cnt
from cte

Первый запрос в предложении with ранжирует записи, имеющие одинаковый id, путем увеличения date; затем рекурсивный запрос обходит набор данных и вычисляет дату начала каждой группы. Последний шаг - нумерация групп и подсчет количества окон.

...