Группировка дат по разнице между строками - PullRequest
0 голосов
/ 01 марта 2019

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

ДАННЫЕ

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

ПРИМЕЧАНИЕ: ЭТО НЕ АКТУАЛЬНЫЕ ПЕРЕМЕННЫЕ ИМЕНА

Ответы [ 2 ]

0 голосов
/ 01 марта 2019

Это то, что известно как проблема "подгонки".В вашем случае вы пытаетесь вписать свои данные в группы, каждая из которых может содержать данные за 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
)
0 голосов
/ 01 марта 2019

Посмотрите на предыдущий ряд.Посмотрите, будет ли разница дат больше или равна 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.

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