Группировать даты по разнице между строками

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

ДАННЫЕ

IDE     DATE
------  ----------
AA1111  23-05-2016
AA1111  25-05-2016
AA1111  25-05-2016
AA1111  13-09-2016
AA1111  02-11-2016
AA1111  23-11-2016
AA1111  06-02-2017
AA1111  06-06-2017
AA1111  01-09-2017
AA1111  12-10-2017
AA1111  17-04-2018
AA1111  25-05-2018
AA1111  05-06-2018

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

SELECT  T.IDE, 
        T.DATE, 
        MAX(T.DATE) OVER (ORDER BY DATE ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING ) - T.DATE AS DIF 

    FROM TESTPAT1 T ;

ВЫХОД 1

IDE     DATE        DIF
------  ----------  ---
AA1111  23-05-2016  2
AA1111  25-05-2016  0
AA1111  25-05-2016  111
AA1111  13-09-2016  50
AA1111  02-11-2016  21
AA1111  23-11-2016  75
AA1111  06-02-2017  120
AA1111  06-06-2017  87
AA1111  01-09-2017  41
AA1111  12-10-2017  187
AA1111  17-04-2018  38
AA1111  25-05-2018  11
AA1111  05-06-2018  0

Отсюда я мог бы использовать разницу между строками, но окно в 16 дней - моя проблема, поскольку каждая дата в группе должна быть внутри этого окна, начиная с первой даты окна.

Некоторые вещи, на которые следует обратить внимание: даты отсортированы по возрастанию, и мой ожидаемый результат будет таким:

ОЖИДАЕМЫЙ РЕЗУЛЬТАТ

IDE     DATE        GROUP

AA1111  23-05-2016  1
AA1111  25-05-2016  1
AA1111  25-05-2016  1
AA1111  13-09-2016  2
AA1111  02-11-2016  3
AA1111  23-11-2016  4
AA1111  06-02-2017  5
AA1111  06-06-2017  6
AA1111  01-09-2017  7
AA1111  12-10-2017  8
AA1111  17-04-2018  9
AA1111  25-05-2018  10
AA1111  05-06-2018  10

ПРИМЕЧАНИЕ: ЭТО НЕ НАСТОЯЩИЕ ИМЕНА ПЕРЕМЕННЫХ

ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
0
47
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Посмотрите на предыдущий ряд. Посмотрите, не превышает ли разница дат 16 дней. Если да, то начинается новая группа. Тогда идентификатор группы представляет собой сумму этих значений «начальной группы».

В SQL:

select t.*,
       sum(case when prev_date > date - interval '16' day then 0 else 1 end) over (partition by ide order by date) as grp
from (select t.*, 
             lag(date) over (partition by ide order by date) as prev_date
      from TESTPAT1 T
     ) t;

Примечание. Это предполагает, что вам действительно нужны отдельные группы для каждого ide. Если это не так, то удалите пункты partition by.

Это довольно близко, однако, если бы дата '09-06-2016' была в данных, ей было бы присвоено значение 1, а поскольку '23-05-2016' является первой датой окна, а разница между ними составляет 17, дата '09-06-2016' должна быть группой 2.

Rolando cq 28.02.2019 22:45

Проблема заключается не в сравнении со следующей строкой, а в сравнении с неизвестным количеством строк на основе разницы дат.

Rolando cq 28.02.2019 22:46

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

Gordon Linoff 28.02.2019 22:47

Это сложная проблема для объяснения, так как я не говорю по-английски. Если бы в данных были все дни января, то с 1 по 16 должна быть группа 1, остальные должны быть группой 2

Rolando cq 28.02.2019 22:49

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

Gordon Linoff 28.02.2019 23:43

Случаи того, что я объясняю, на самом деле являются некоторыми исключениями, поэтому я собираюсь подойти к проблеме с этим решением.

Rolando cq 01.03.2019 13:27
Ответ принят как подходящий

Это то, что известно как проблема «подгонки битов». В вашем случае вы пытаетесь поместить свои данные в группы, каждая из которых может содержать данные за 16 дней.

Существует несколько хорошо известных способов использования SQL для решения проблем с подгонкой контейнеров. MATCH RECOGNIZE так же хорош, как любой из них:

with test_data (IDE,     "DATE") AS (
SELECT 'AA1111',  TO_DATE('23-05-2016','DD-MM-YYYY') FROM DUAL UNION ALL
SELECT 'AA1111',  TO_DATE('25-05-2016','DD-MM-YYYY') FROM DUAL UNION ALL
SELECT 'AA1111',  TO_DATE('25-05-2016','DD-MM-YYYY') FROM DUAL UNION ALL
SELECT 'AA1111',  TO_DATE('13-09-2016','DD-MM-YYYY') FROM DUAL UNION ALL
SELECT 'AA1111',  TO_DATE('02-11-2016','DD-MM-YYYY') FROM DUAL UNION ALL
SELECT 'AA1111',  TO_DATE('23-11-2016','DD-MM-YYYY') FROM DUAL UNION ALL
SELECT 'AA1111',  TO_DATE('06-02-2017','DD-MM-YYYY') FROM DUAL UNION ALL
SELECT 'AA1111',  TO_DATE('06-06-2017','DD-MM-YYYY') FROM DUAL UNION ALL
SELECT 'AA1111',  TO_DATE('01-09-2017','DD-MM-YYYY') FROM DUAL UNION ALL
SELECT 'AA1111',  TO_DATE('12-10-2017','DD-MM-YYYY') FROM DUAL UNION ALL
SELECT 'AA1111',  TO_DATE('17-04-2018','DD-MM-YYYY') FROM DUAL UNION ALL
SELECT 'AA1111',  TO_DATE('25-05-2018','DD-MM-YYYY') FROM DUAL UNION ALL
SELECT 'AA1111',  TO_DATE('05-06-2018','DD-MM-YYYY') FROM DUAL )
SELECT ide, "DATE", mno as "GROUP"
FROM test_data
match_recognize (
  partition by ide
  order by "DATE"
  measures 
    match_number() as mno,
    "DATE" - FIRST(GRP."DATE") as dif
    all rows per match
    pattern (  grp* )
    define 
      grp AS "DATE" - FIRST("DATE") < 16
  );

Результаты

+--------+-----------+-------+
|  IDE   |   DATE    | GROUP |
+--------+-----------+-------+
| AA1111 | 23-MAY-16 |     1 |
| AA1111 | 25-MAY-16 |     1 |
| AA1111 | 25-MAY-16 |     1 |
| AA1111 | 13-SEP-16 |     2 |
| AA1111 | 02-NOV-16 |     3 |
| AA1111 | 23-NOV-16 |     4 |
| AA1111 | 06-FEB-17 |     5 |
| AA1111 | 06-JUN-17 |     6 |
| AA1111 | 01-SEP-17 |     7 |
| AA1111 | 12-OCT-17 |     8 |
| AA1111 | 17-APR-18 |     9 |
| AA1111 | 25-MAY-18 |    10 |
| AA1111 | 05-JUN-18 |    10 |
+--------+-----------+-------+

Обновление для пользователей 11g с использованием пункта MODEL

Этот запрос должен работать на 11g, чтобы решить вашу проблему с установкой мусорного ведра. Те же результаты, что и выше, просто другой подход.

with 
  -- First, sort the input data because we need to be able to refer
  -- to the prior row and `lag` doesn't really work in `MODEL`, afaik.
sorted_inputs ( ide, sort_order, "DATE", first_date_in_group, grp, diff) as
( SELECT ide, 
         row_number() over ( partition by ide order by "DATE" ) sort_order, 
         "DATE", 
         -- These columns are place holders for the MODEL clause to update
         CAST(NULL AS DATE) first_date_in_group, 
         0 grp, 
         0 diff 
  FROM   test_data )
SELECT  ide, "DATE", grp "GROUP"
from    sorted_inputs
model 
partition by (ide)
dimension by (sort_order)
measures ( "DATE", grp, first_date_in_group, diff )
rules update automatic order
( grp[1] = 1,
  first_date_in_group[1] = "DATE"[1],
  diff[ANY] = "DATE"[CV()] - first_date_in_group[CV()-1],
  grp[sort_order>1] = grp[cv()-1] + CASE WHEN diff[CV()] > 16 THEN 1 ELSE 0 END,
  first_date_in_group[sort_order>1] = CASE WHEN diff[CV()] > 16 THEN "DATE"[CV()] ELSE first_date_in_group[CV()-1] END
)

Спасибо за ответ. Кажется, это хороший ответ, но я использую 11g, поэтому не могу его использовать.

Rolando cq 01.03.2019 13:26

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

Matthew McPeak 01.03.2019 13:34

@Rolandocq Я обновил свой ответ решением для 11g.

Matthew McPeak 01.03.2019 16:27

Вау, это то, что мне было нужно, спасибо

Rolando cq 01.03.2019 17:02

Другие вопросы по теме