Как использовать GROUP BY для объединения строк в SQL Server? - PullRequest
339 голосов
/ 07 ноября 2008

Как мне получить:

id       Name       Value
1          A          4
1          B          8
2          C          9

до

id          Column
1          A:4, B:8
2          C:9

Ответы [ 16 ]

509 голосов
/ 07 ноября 2008

Нет необходимости в курсоре, цикле WHILE или пользовательской функции .

Просто нужно быть креативным с FOR XML и PATH.

[Примечание. Это решение работает только в SQL 2005 и более поздних версиях. В исходном вопросе не указана используемая версия.]

CREATE TABLE #YourTable ([ID] INT, [Name] CHAR(1), [Value] INT)

INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'A',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'B',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)

SELECT 
  [ID],
  STUFF((
    SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX)) 
    FROM #YourTable 
    WHERE (ID = Results.ID) 
    FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
  ,1,2,'') AS NameValues
FROM #YourTable Results
GROUP BY ID

DROP TABLE #YourTable
65 голосов
/ 27 апреля 2017

Если это SQL Server 2017 или SQL Server Vnext, SQL Azure вы можете использовать string_agg, как показано ниже:

select id, string_agg(concat(name, ':', [value]), ', ')
    from #YourTable 
    group by id
49 голосов
/ 06 декабря 2011

использование пути XML не будет идеально объединено, как вы могли бы ожидать ... оно заменит "&" на "& amp;" а также будет связываться с <" and "> ... может быть несколько других вещей, не уверен ... но вы можете попробовать это

Я нашел обходной путь для этого ... вам нужно заменить:

FOR XML PATH('')
)

с:

FOR XML PATH(''),TYPE
).value('(./text())[1]','VARCHAR(MAX)')

... или NVARCHAR(MAX), если это то, что вы используете.

почему, черт возьми, SQL не имеет объединенную функцию агрегирования? это пита.

38 голосов
/ 18 октября 2011

Я столкнулся с парой проблем, когда попытался преобразовать предложение Кевина Фэйрчайлда для работы со строками, содержащими пробелы и специальные символы XML (&, <, >), которые были закодированы.

Окончательная версия моего кода (которая не отвечает на первоначальный вопрос, но может быть полезна кому-то) выглядит следующим образом:

CREATE TABLE #YourTable ([ID] INT, [Name] VARCHAR(MAX), [Value] INT)

INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'Oranges & Lemons',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'1 < 2',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)

SELECT  [ID],
  STUFF((
    SELECT ', ' + CAST([Name] AS VARCHAR(MAX))
    FROM #YourTable WHERE (ID = Results.ID) 
    FOR XML PATH(''),TYPE 
     /* Use .value to uncomment XML entities e.g. &gt; &lt; etc*/
    ).value('.','VARCHAR(MAX)') 
  ,1,2,'') as NameValues
FROM    #YourTable Results
GROUP BY ID

DROP TABLE #YourTable

Вместо использования пробела в качестве разделителя и замены всех пробелов запятыми, он просто добавляет запятую и пробел к каждому значению, а затем использует STUFF для удаления первых двух символов.

Кодировка XML обеспечивается автоматически с помощью директивы TYPE .

21 голосов
/ 10 июня 2010

Еще один вариант использования Sql Server 2005 и выше

---- test data
declare @t table (OUTPUTID int, SCHME varchar(10), DESCR varchar(10))
insert @t select 1125439       ,'CKT','Approved'
insert @t select 1125439       ,'RENO','Approved'
insert @t select 1134691       ,'CKT','Approved'
insert @t select 1134691       ,'RENO','Approved'
insert @t select 1134691       ,'pn','Approved'

---- actual query
;with cte(outputid,combined,rn)
as
(
  select outputid, SCHME + ' ('+DESCR+')', rn=ROW_NUMBER() over (PARTITION by outputid order by schme, descr)
  from @t
)
,cte2(outputid,finalstatus,rn)
as
(
select OUTPUTID, convert(varchar(max),combined), 1 from cte where rn=1
union all
select cte2.outputid, convert(varchar(max),cte2.finalstatus+', '+cte.combined), cte2.rn+1
from cte2
inner join cte on cte.OUTPUTID = cte2.outputid and cte.rn=cte2.rn+1
)
select outputid, MAX(finalstatus) from cte2 group by outputid
13 голосов
/ 19 марта 2016

Установка агрегатов SQLCLR из http://groupconcat.codeplex.com

Затем вы можете написать такой код, чтобы получить результат, который вы просили:

CREATE TABLE foo
(
 id INT,
 name CHAR(1),
 Value CHAR(1)
);

INSERT  INTO dbo.foo
    (id, name, Value)
VALUES  (1, 'A', '4'),
        (1, 'B', '8'),
        (2, 'C', '9');

SELECT  id,
    dbo.GROUP_CONCAT(name + ':' + Value) AS [Column]
FROM    dbo.foo
GROUP BY id;
12 голосов
/ 20 ноября 2008

SQL Server 2005 и более поздние версии позволяют вам создавать свои собственные пользовательские агрегатные функции , в том числе для таких вещей, как конкатенация - см. Образец внизу связанной статьи.

10 голосов
/ 11 февраля 2017

Восемь лет спустя ... Ядро базы данных Microsoft SQL Server vNext наконец-то улучшило Transact-SQL, чтобы напрямую поддерживать объединение сгруппированных строк. В техническом обзоре сообщества версии 1.0 добавлена ​​функция STRING_AGG, а в CTP 1.1 добавлено предложение WITHIN GROUP для функции STRING_AGG.

Ссылка: https://msdn.microsoft.com/en-us/library/mt775028.aspx

9 голосов
/ 01 февраля 2013

Примером будет

В Oracle вы можете использовать агрегатную функцию LISTAGG.

Оригинальные записи

name   type
------------
name1  type1
name2  type2
name2  type3

Sql

SELECT name, LISTAGG(type, '; ') WITHIN GROUP(ORDER BY name)
FROM table
GROUP BY name

Результат в

name   type
------------
name1  type1
name2  type2; type3
7 голосов
/ 07 ноября 2008

Просто, чтобы добавить к тому, что сказал Кейд, это, как правило, интерфейсный дисплей, и поэтому его следует обрабатывать там. Я знаю, что иногда проще написать что-то на 100% в SQL для таких вещей, как экспорт файлов или другие решения «только для SQL», но в большинстве случаев эта конкатенация должна обрабатываться на вашем уровне отображения.

...