SQL: как объединить каждую группу из N строк в одну строку

у меня есть это

 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 (например, последняя)


person Philippe    schedule 11.06.2019    source источник


Ответы (2)


Стандартное 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(), поэтому вам придется использовать +. И это дает ; для разделителя, а не ,, но это довольно близко.

person Gordon Linoff    schedule 11.06.2019
comment
Вау спасибо за быстрый ответ. Я много читал о sybase и, насколько я понимаю, вам нужны специальные процедуры... - person Philippe; 11.06.2019

Удивительный сценарий. Но я подозреваю, что вы пропустили AS. Поэтому я сделал это так:

выберите string_agg(t.[id], ',') в качестве идентификаторов, string_agg(t.[name], ',') в качестве имен из

(выбрать t.*, row_number() вместо (упорядочить по идентификатору) как seqnum из [tablename] ) AS t

сгруппировать по приведению ((seqnum - 1)/3 as int);

В моем случае это так (хотя я не мог заставить «внутри группы (порядок по идентификатору)» работать каким-либо образом..... хммм)

Вот мой, который хорошо работает, это список электронных писем всем моим ученикам, объединенный в строку для каждых 100 строк. К сожалению, String_Agg ограничивает его до 8000 символов. Кто-нибудь знает альтернативу String_Agg для SQL Server?

ВЫБЕРИТЕ string_agg(t.[Имя учащегося], ';') как [Все имена], string_agg(t.[Электронная почта учащегося], ';') как [Все электронные письма]

FROM ( SELECT [Имя учащегося], [Электронная почта учащегося], ROW_NUMBER() OVER (ORDER BY [Электронная почта учащегося]) AS RowNo FROM [Список рассылки для курсов], где [Имя продукта] = 'Онлайн-курсы') AS t сгруппировать по составу ((RowNo - 1)/100 как целое число);

Надеюсь поможет ‹3

person Andrew Boey Sau Chung    schedule 16.02.2021
comment
Пожалуйста, поймите, что раздел ответов предназначен для ответов. Если у вас есть ответ, опубликуйте один ответ, который включает в себя всю информацию, автономную. Не разбивайте попытку ответа на несколько ответов. Это не то, как работает переполнение стека. Ваши сообщения очень сбивают с толку, поскольку они разбиты на части, но больше похоже на то, что у вас есть вопрос, поэтому, пожалуйста, опубликуйте новый вопрос, снова автономный. Вы также можете дать ссылку на этот пост, если считаете, что он дает контекст. - person costaparas; 16.02.2021
comment
Кроме того, посетите страницу справки по форматированию, чтобы улучшить форматирование. - person costaparas; 16.02.2021
comment
Это не дает ответа на вопрос. Получив достаточную репутацию, вы сможете /comment">прокомментировать любой пост; вместо этого дайте ответы которые не требуют разъяснений от спрашивающего. – Из обзора - person Jin Lee; 16.02.2021