SQL: как объединить каждую группу из N строк в одну строку - PullRequest
1 голос
/ 11 июня 2019

У меня есть

 ID | Name 
----+-------
 31 | Abby
 24 | Bruce 
 44 | Carl 
 49 | Derek
 55 | Eric
 81 | Fred

Я хочу объединить группы из N строк в одну строку. Для N = 3 это дало бы мне

    ID    |    Name 
----------+----------------
 31,24,44 | Abby,Bruce,Carl
 49,55,81 | Derek,Eric,Fred

Мне удалось создать строку для использования GROUP BY и CONCAT, но она работает только в mysql ...

SET @row_number = 0;
SELECT *, (@row_number:=@row_number + 1) AS r1, (@row_number - 1) DIV 3 AS r2 FROM table1

 ID | Name  | r1| r2
----+-------+---+---
 31 | Abby  | 1 | 0
 24 | Bruce | 2 | 0
 44 | Carl  | 3 | 0
 49 | Derek | 4 | 1
 55 | Eric  | 5 | 1
 81 | Fred  | 6 | 1

Для уточнения:

  • Мне нужно ванильное SQL-решение (поэтому оно будет работать в mysql, sybase, oracle и postgres)

  • Мне не нужен заказ, я просто хочу восстановить исходную таблицу в какой-то момент

  • У меня нет прав на запись на этой базе, только чтение

  • Я хочу объединить столбцы любого типа (приведя их к строке) и обработать NULL

  • Это нормально, если некоторые группы точно не имеют размер N (как и последняя)

1 Ответ

1 голос
/ 11 июня 2019

Стандартное решение SQL выглядит примерно так:

select listagg(id, ',') within group (order by id) as ids,
       listagg(name, ',') within group (order by id) as names
from (select t.*, row_number() over (order by id) as seqnum
      from t
     ) t
group by cast( (seqnum - 1) / 3 as int);

Я думаю, что это будет работать как есть в Oracle. В MySQL вам нужно изменить listagg() на group_concat() (и использовать MySQL 8+), а в Postgres вам нужно изменить listagg() на string_agg().

И вы в значительной степени не можете сделать это в Sybase.

Ой, подождите, есть другой способ:

select concat( (case when seqnum % 3 = 1 then concat(id, ';') else '' end),
               (case when seqnum % 3 = 2 then concat(id, ';') else '' end),
               (case when seqnum % 3 = 0 then concat(id, ';') else '' end)
             ) as ids,
       concat( (case when seqnum % 3 = 1 then concat(name, ';') else '' end),
               (case when seqnum % 3 = 2 then concat(name, ';') else '' end),
               (case when seqnum % 3 = 0 then concat(name, ';') else '' end)
             ) as name           
from (select t.*, row_number() over (order by id) as seqnum
      from t
     ) t
group by cast( (seqnum - 1) / 3 as int);

Конечно, Sybase не поддерживает concat(), поэтому вы должны использовать +. И это дает ; для разделителя, а не ,, но это довольно близко.

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