O T-SQL tem uma function agregada para concatenar strings?

Duplicatas possíveis:
Função de tipo de implemento no SQL Server 2000?
Concatenar valores de linha T-SQL

Eu tenho uma visão que estou consultando que se parece com isso:

BuildingName PollNumber ------------ ---------- Foo Centre 12 Foo Centre 13 Foo Centre 14 Bar Hall 15 Bar Hall 16 Baz School 17 

Eu preciso escrever uma consulta que agrupa BuildingNames juntos e exibe uma lista de PollNumbers como este:

 BuildingName PollNumbers ------------ ----------- Foo Centre 12, 13, 14 Bar Hall 15, 16 Baz School 17 

Como posso fazer isso no T-SQL? Eu prefiro não recorrer a escrever um procedimento armazenado para isso, já que parece um exagero, mas eu não sou exatamente uma pessoa do database. Parece que uma function agregada como SUM () ou AVG () é o que eu preciso, mas não sei se o T-SQL tem um. Estou usando o SQL Server 2005.

para o SQL Server 2017 e use:

STRING_AGG ()

 set nocount on; declare @YourTable table (RowID int, HeaderValue int, ChildValue varchar(5)) insert into @YourTable VALUES (1,1,'CCC') insert into @YourTable VALUES (2,2,'B<&>B') insert into @YourTable VALUES (3,2,'AAA') insert into @YourTable VALUES (4,3,'
') insert into @YourTable VALUES (5,3,'A & Z') set nocount off SELECT t1.HeaderValue ,STUFF( (SELECT ', ' + t2.ChildValue FROM @YourTable t2 WHERE t1.HeaderValue=t2.HeaderValue ORDER BY t2.ChildValue FOR XML PATH(''), TYPE ).value('.','varchar(max)') ,1,2, '' ) AS ChildValues FROM @YourTable t1 GROUP BY t1.HeaderValue SELECT HeaderValue, STRING_AGG(ChildValue,', ') FROM @YourTable GROUP BY HeaderValue

SAÍDA:

 HeaderValue ----------- ------------- 1 CCC 2 B<&>B, AAA 3 
, A & Z (3 rows affected)

para o SQL Server 2005 e até 2016, você precisa fazer algo assim:

 --Concatenation with FOR XML and eleminating control/encoded character expansion "& < >" set nocount on; declare @YourTable table (RowID int, HeaderValue int, ChildValue varchar(5)) insert into @YourTable VALUES (1,1,'CCC') insert into @YourTable VALUES (2,2,'B<&>B') insert into @YourTable VALUES (3,2,'AAA') insert into @YourTable VALUES (4,3,'
') insert into @YourTable VALUES (5,3,'A & Z') set nocount off SELECT t1.HeaderValue ,STUFF( (SELECT ', ' + t2.ChildValue FROM @YourTable t2 WHERE t1.HeaderValue=t2.HeaderValue ORDER BY t2.ChildValue FOR XML PATH(''), TYPE ).value('.','varchar(max)') ,1,2, '' ) AS ChildValues FROM @YourTable t1 GROUP BY t1.HeaderValue

SAÍDA:

 HeaderValue ChildValues ----------- ------------------- 1 CCC 2 AAA, B<&>B 3 
, A & Z (3 row(s) affected)

Além disso, cuidado, nem todas as concatenações FOR XML PATH manipularão corretamente caracteres especiais XML como o exemplo acima.

Não há nenhuma function incorporada no Sql Server, mas pode ser obtida escrevendo um agregado definido pelo usuário. Este artigo menciona essa function como parte dos exemplos do SQL Server: http://msdn.microsoft.com/en-us/library/ms182741.aspx

Como exemplo, incluo o código para um agregado Concatenate. Para usá-lo, crie um projeto de database no Visual Studio, adicione o novo SqlAggregate e substitua o código pelo exemplo abaixo. Depois de implantado, você deve encontrar um novo assembly em seu database e uma function agregada Concatenate

 using System; using System.Data.SqlTypes; using System.IO; using System.Text; using Microsoft.SqlServer.Server; [Serializable] [SqlUserDefinedAggregate(Format.UserDefined, IsInvariantToNulls = true, IsInvariantToDuplicates = false, IsInvariantToOrder = false, MaxByteSize = 8000, Name = "Concatenate")] public class Concatenate : IBinarySerialize { private StringBuilder _intermediateResult; internal string IntermediateResult { get { return _intermediateResult.ToString(); } } public void Init() { _intermediateResult = new StringBuilder(); } public void Accumulate(SqlString value) { if (value.IsNull) return; _intermediateResult.Append(value.Value); } public void Merge(Concatenate other) { if (null == other) return; _intermediateResult.Append(other._intermediateResult); } public SqlString Terminate() { var output = string.Empty; if (_intermediateResult != null && _intermediateResult.Length > 0) output = _intermediateResult.ToString(0, _intermediateResult.Length - 1); return new SqlString(output); } public void Read(BinaryReader reader) { if (reader == null) throw new ArgumentNullException("reader"); _intermediateResult = new StringBuilder(reader.ReadString()); } public void Write(BinaryWriter writer) { if (writer == null) throw new ArgumentNullException("writer"); writer.Write(_intermediateResult.ToString()); } } 

Para usá-lo, você pode simplesmente escrever uma consulta agregada:

 create table test( id int identity(1,1) not null primary key , class tinyint not null , name nvarchar(120) not null ) insert into test values (1, N'This'), (1, N'is'), (1, N'just'), (1, N'a'), (1, N'test'), (2, N','), (3, N'do'), (3, N'not'), (3, N'be'), (3, N'alarmed'), (3, N','), (3, N'this'), (3, N'is'), (3, N'just'), (3, N'a'), (3, N'test') select dbo.Concatenate(name + ' ') from test group by class drop table test 

A saída da consulta é:

 -- Output -- =================== -- This is just a test -- , -- do not be alarmed , this is just a test 

Eu empacotei a class e o agregado como um script que você pode encontrar aqui: https://gist.github.com/FilipDeVos/5b7b4addea1812067b09