Группировать соседние строки с одинаковым значением - PullRequest
0 голосов
/ 13 июля 2020

В следующей таблице: https://www.db-fiddle.com/f/avcRnMG6SVAoRuV5Rf4znF/2

create table tbl
   (id integer,
    start integer,
    stop integer,
    type integer);
    
    INSERT INTO tbl values
    
    (101, 1, 3, 10),
    (101, 3, 6, 15),
    (101, 6, 10, 17),
    (101, 10, 40, 20),
    (101, 40, 100, 20),
    (101, 100, 200, 20),
    (101, 200, 500, 55);

Я хочу сгруппировать соседние строки с одинаковым значением с граничными значениями start и stop. Таким образом, вместо 3 строк с type 20 в результатах должно быть

101, 10, 200, 20

Я пробовал что-то вроде этого, но это далеко не хорошо, есть ли какое-то умное, короткое решение?

SELECT 
    id, 
   case when lag(type) OVER (partition by id ORDER BY start ) = type 
   then lag(start) OVER (partition by id ORDER BY start) else start end as from
   , case when lead(type) OVER (partition by id ORDER BY start ) = type 
   then lead(stop) OVER (partition by id ORDER BY start) else stop end as to
   , type
   
from tbl

Ответы [ 2 ]

1 голос
/ 13 июля 2020

Значения type кажутся постоянно увеличивающимися, поэтому они не повторяются. Итак, похоже, что простого group by будет достаточно:

select id, min(start), max(stop), type
from tbl
group by id, type;

Если типы можно перемежать, то вам нужно рассматривать это как проблему промежутков и островов, используя более сложные запросы.

1 голос
/ 13 июля 2020

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

здесь скрипт

  1. Аннотировать записи, где type изменяется
  2. Используйте sum(), чтобы назначить строки группам, где type остается неизменным в последовательных строках
  3. Сверните значения start и stop для повторяющихся групп

with changes as (
  select *, 
         case 
           when lag(type) over (partition by id
                                    order by start) = type then 0
           else 1
         end as changed
    from tbl
), groups as (
  select *,
         sum(changed) over (partition by id
                                order by start) as grp
    from changes
), combined as (
  select id, 
         min(start) as start,
         max(stop) as stop,
         type 
    from groups
   group by id, grp, type
)
--select * from changes order by start;
--select * from groups order by start;
select * from combined order by start;
...