Группировать похожие строки и считать группы в PostgreSQL - PullRequest
0 голосов
/ 13 июня 2018

У меня есть такая таблица:

number | info | side
--------------------
     1 |  foo |    a
     2 |  bar |    a
     3 |  bar |    a
     4 |  baz |    a
     5 |  foo |    a
     6 |  bar |    b
     7 |  bar |    b
     8 |  foo |    a
     9 |  bar |    a
    10 |  baz |    a

Я бы хотел узнать, сколько раз bar группа / пакет (например, строки 2,3 - это группа, строки 6,7 - группа, строка 9 - также группа) появляется в столбце info в зависимости от side.Я застрял, потому что я действительно не знаю, что делать Google.Всякий раз, когда я ищу что-то вроде group rows или merge rows, я всегда заканчиваю тем, что нахожу информацию о функции group by.

Однако я думаю, что мне нужна какая-то оконная функция.

Здесьэто то, чего я хотел бы достичь:

bar_a | bar_b
-------------
    2 |     1

Ответы [ 3 ]

0 голосов
/ 13 июня 2018

Это проблема "пробелов и островов", по сути, если я правильно понимаю.Для этой версии разница номеров строк должна хорошо работать.

select sum( (side = 'a')::int) as num_a,
       sum( (side = 'b')::int) as num_b
from (select info, side, count(*) as cnt
      from (select t.*,
                   row_number() over (order by number) as seqnum,
                   row_number() over (partition by info, side order by number) as seqnum_bs
            from t
           ) t
      where info = 'bar'
      group by info, size, (seqnum - seqnum_bs)
     ) si;
0 голосов
/ 13 июня 2018

Вы можете обойтись с помощью функции single window, которая должна быть самой быстрой:

SELECT side, count(*) AS count
FROM  (
   SELECT side, grp
   FROM  (
      SELECT side, number - row_number() OVER (PARTITION BY side ORDER BY number) AS grp
      FROM   tbl
      WHERE  info = 'bar'
      ) sub1
   GROUP BY 1, 2
   ) sub2
GROUP BY 1
ORDER BY 1;  -- optional

Или короче, может быть, не быстрее:

SELECT side, count(DISTINCT grp) AS count
FROM  (
   SELECT side, number - row_number() OVER (PARTITION BY side ORDER BY number) AS grp
   FROM   tbl
   WHERE  info = 'bar'
   ) sub
GROUP BY 1
ORDER BY 1;  -- optional

«Хитрость» заключается в том, что соседние строки, образующие группу (grp), имеют последовательные номера.При вычитании счетчика прогонов по разделу в side из счетчика прогонов по всем строкам (number), члены «группы» получают одинаковое число grp.

При наличии пробелы в вашем последовательном столбце number, что не так в вашей демоверсии, но обычно есть пробелы (и вы действительно хотите игнорировать такие пробелы ?!), затем используйте row_number() OVER (ORDER BY number) в подзапросе вместо простоnumber чтобы сначала закрыть пробелы:

SELECT side, count(DISTINCT grp) AS count
FROM  (
   SELECT side, number - row_number() OVER (PARTITION BY side ORDER BY number) AS grp
   FROM  (SELECT info, side, row_number() OVER (ORDER BY number) AS number FROM tbl) tbl1
   WHERE  info = 'bar'
   ) sub2
GROUP BY 1
ORDER BY 1;  -- optional

SQL Fiddle (с расширенным контрольным примером)

Похожие:

0 голосов
/ 13 июня 2018

Используйте lag() для определения первых строк групп:

select 
    number, info, side, 
    lag(info || side, 1, '') over (order by number) <> info || side as start_of_group
from my_table
order by 1;

 number | info | side | start_of_group 
--------+------+------+----------------
      1 | foo  | a    | t
      2 | bar  | a    | t
      3 | bar  | a    | f
      4 | baz  | a    | t
      5 | foo  | a    | t
      6 | bar  | b    | t
      7 | bar  | b    | f
      8 | foo  | a    | t
      9 | bar  | a    | t
     10 | baz  | a    | t
(10 rows)

Объедините и отфильтруйте приведенный выше результат, чтобы получить желаемый результат:

select concat(info, '_', side) as info_side, count(*)
from (
    select 
        info, side, 
        lag(info || side, 1, '') over (order by number) <> info || side as start_of_group
    from my_table
    ) s
where info = 'bar' and start_of_group
group by 1
order by 1;

 info_side | count 
-----------+-------
 bar_a     |     2
 bar_b     |     1
(2 rows)    
...