Группируйте строки в диапазоне, также показывая разрыв - PullRequest
1 голос
/ 21 апреля 2020

Мне нужно решение для выбора базы данных, чтобы преобразовать серию подробностей в сводную версию, возможно, в виде. Рассмотрим таблицу ниже. Он имеет составной первичный ключ (PK_1, PK_2, PK_3 и SEQUENCE_NO).

PK_1     PK_2 PK_3 SEQUENCE_NO STATUS_CODE
======== ==== ==== =========== ===========
20200421 A    1    1           Y
20200421 A    1    2           Y
20200421 A    1    3           Y
20200421 A    1    4           N
20200421 A    1    5           Y
20200421 A    1    6           Y
20200421 A    2    7           Y
20200421 A    2    8           Y
20200421 B    3    9           Y
20200421 B    3    10          Y
20200421 B    3    11          Y
20200422 B    3    11          Y

Только включая все записи с STATUS_CODE из "Y", как можно ли представить записи таким образом, чтобы последовательные записи, соответствующие их составным первичным ключам, формировали диапазоны значений (обозначенные SEQUENCE_FROM и SEQUENCE_TO, см. ниже), показывая при этом пробел, который может указывать на отсутствующую строку, или строка с STATUS_CODE со значением, отличным от «Y»?

PK_1     PK_2 PK_3 SEQUENCE_FROM SEQUENCE_TO
======== ==== ==== ============= ===========
20200421 A    1    1             3
20200421 A    1    5             6
20200421 A    2    7             8
20200421 B    3    9             11
20200422 B    3    11            11

Я использовал MIN и MAX, но, очевидно, это не будет соответствовать показу разрыва между диапазонами.

Ответы [ 3 ]

2 голосов
/ 21 апреля 2020

Это проблема пробелов и островков. Вот один из способов решения с помощью ROW_NUMBER, используя метод разности номеров строк:

WITH cte AS (
    SELECT t.*, SEQUENCE_NO -
        ROW_NUMBER() OVER (PARTITION BY PK_1, PK_2, PK_3 ORDER BY SEQUENCE_NO) AS diff
    FROM yourTable t
    WHERE STATUS_CODE = 'Y'
)


SELECT
    PK_1,
    PK_2,
    PK_3,
    MIN(SEQUENCE_NO) AS SEQUENCE_FROM,
    MAX(SEQUENCE_NO) AS SEQUENCE_TO
FROM cte
GROUP BY
    PK_1,
    PK_2,
    PK_3,
    (rn1 - rn2)
ORDER BY
    PK_1,
    PK_2,
    PK_3;

screen capture of demo below

Демо

Суть логики c, используемой здесь, заключается в том, что мы формируем номер группы «на лету» для каждого острова, внутри каждого PK_1, PK_2, PK_3 разделить, взяв разницу между порядковым номером и ROW_NUMBER. Эта разница гарантированно всегда будет уникальной для каждого острова.

1 голос
/ 21 апреля 2020

Вот один из способов сделать это, используя предложение match_recognize (Oracle 12.1 и выше). Если я правильно понимаю, «пробел» существует, когда существует одна или несколько строк с кодом состояния «N» - и только в том случае, если перед такими строками предшествует строка «Y» для той же комбинации pk_1, pk_2, pk_3. Таким образом, в ваших выборочных данных есть только один такой пробел. Вы не объяснили, что именно вы хотите показать (я предполагаю число, но вы не объяснили, как его вычислить). Я интерпретировал это как разницу между начальным значением «текущей» последовательности и конечным значением «предыдущей» последовательности.

with
  yourtable (pk_1, pk_2, pk_3, sequence_no, status_code) as (
    select 20200421, 'A', 1,  1, 'Y' from dual union all
    select 20200421, 'A', 1,  2, 'Y' from dual union all
    select 20200421, 'A', 1,  3, 'Y' from dual union all
    select 20200421, 'A', 1,  4, 'N' from dual union all
    select 20200421, 'A', 1,  5, 'Y' from dual union all
    select 20200421, 'A', 1,  6, 'Y' from dual union all
    select 20200421, 'A', 2,  7, 'Y' from dual union all
    select 20200421, 'A', 2,  8, 'Y' from dual union all
    select 20200421, 'B', 3,  9, 'Y' from dual union all
    select 20200421, 'B', 3, 10, 'Y' from dual union all
    select 20200421, 'B', 3, 11, 'Y' from dual union all
    select 20200422, 'B', 3, 11, 'Y' from dual
  )
select mr.*
     , sequence_from - lag(sequence_to) over (partition by pk_1, pk_2, pk_3 
                                              order by sequence_from) as gap
from   yourtable
match_recognize(
  partition by pk_1, pk_2, pk_3
  order     by sequence_no
  measures  first(sequence_no) as sequence_from
         ,  last (sequence_no) as sequence_to
  pattern   ( Y+ )
  define    Y as status_code = 'Y'
) mr
;

Вывод:

      PK_1 PK_2       PK_3 SEQUENCE_FROM SEQUENCE_TO        GAP    
---------- ---- ---------- ------------- ----------- ----------
  20200421 A             1             1           3           
  20200421 A             1             5           6          2
  20200421 A             2             7           8           
  20200421 B             3             9          11           
  20200422 B             3            11          11           
1 голос
/ 21 апреля 2020

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

select
    PK_1,
    PK_2,
    PK_3,
    min(sequence_no) as SEQUENCE_FROM,
    max(sequence_no) as SEQUENCE_TO
from
(
    select
        *,
        sequence_no - row_number() over (partition by STATUS_CODE, PK_3 order by SEQUENCE_NO) as rnk
    from myTable
    where STATUS_CODE = 'Y'
) t    
group by
    PK_1,
    PK_2,
    PK_3,
    rnk

Вывод:

pk_1     pk_2 pk_3 sequence_from sequence_to
---------------------------------------------
20200421  A     1       1             3
20200421  A     1       5             6
20200421  A     2       7             8
20200421  B     3       9             11
20200422  B     3       11            11
...