texto string_agg operadores concatenar concatenación caracteres campos cadenas sql sql-server string-concatenation sql-server-group-concat

string_agg - sql server concatenar campos



¿Cómo usar GROUP BY para concatenar cadenas en SQL Server? (16)

Como lo consigo:

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

a

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


En Oracle puede usar la función agregada de LISTAGG. Un ejemplo sería:

name type ------------ name1 type1 name2 type2 name2 type3 SELECT name, LISTAGG(type, ''; '') WITHIN GROUP(ORDER BY name) FROM table GROUP BY name

Resultaría en:

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


Este tipo de pregunta se hace aquí muy a menudo, y la solución dependerá mucho de los requisitos subyacentes:

https://.com/search?q=sql+pivot

y

https://.com/search?q=sql+concatenate

Normalmente, no hay una forma solo de SQL para hacer esto sin un SQL dinámico, una función definida por el usuario o un cursor.


Esto es solo una adición a la publicación de Kevin Fairchild (muy inteligente por cierto). Lo habría agregado como un comentario, pero todavía no tengo suficientes puntos :)

Estaba usando esta idea para una vista en la que estaba trabajando, sin embargo, los elementos que estaba concatinando contenían espacios. Así que modifiqué ligeramente el código para no usar espacios como delimitadores.

Nuevamente gracias por la genial solución Kevin!

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], REPLACE(REPLACE(REPLACE( (SELECT [Name] + '':'' + CAST([Value] AS VARCHAR(MAX)) as A FROM #YourTable WHERE ( ID = Results.ID ) FOR XML PATH ('''')) , ''</A><A>'', '', '') ,''<A>'','''') ,''</A>'','''') AS NameValues FROM #YourTable Results GROUP BY ID DROP TABLE #YourTable


Instale los agregados de SQLCLR desde http://groupconcat.codeplex.com

Luego, puede escribir un código como este para obtener el resultado que solicitó:

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;


Me encontré con un par de problemas cuando intenté convertir la sugerencia de Kevin Fairchild para trabajar con cadenas que contenían espacios y caracteres XML especiales ( & , < , > ) que estaban codificados.

La versión final de mi código (que no responde a la pregunta original pero puede ser útil para alguien) se ve así:

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

En lugar de usar un espacio como delimitador y reemplazar todos los espacios con comas, simplemente coloca una coma y espacio para cada valor y luego usa STUFF para eliminar los dos primeros caracteres.

La codificación XML se realiza automáticamente mediante el uso de la directiva TYPE .


No necesita un cursor ... un bucle de tiempo es suficiente.

------------------------------ -- Setup ------------------------------ DECLARE @Source TABLE ( id int, Name varchar(30), Value int ) DECLARE @Target TABLE ( id int, Result varchar(max) ) INSERT INTO @Source(id, Name, Value) SELECT 1, ''A'', 4 INSERT INTO @Source(id, Name, Value) SELECT 1, ''B'', 8 INSERT INTO @Source(id, Name, Value) SELECT 2, ''C'', 9 ------------------------------ -- Technique ------------------------------ INSERT INTO @Target (id) SELECT id FROM @Source GROUP BY id DECLARE @id int, @Result varchar(max) SET @id = (SELECT MIN(id) FROM @Target) WHILE @id is not null BEGIN SET @Result = null SELECT @Result = CASE WHEN @Result is null THEN '''' ELSE @Result + '', '' END + s.Name + '':'' + convert(varchar(30),s.Value) FROM @Source s WHERE id = @id UPDATE @Target SET Result = @Result WHERE id = @id SET @id = (SELECT MIN(id) FROM @Target WHERE @id < id) END SELECT * FROM @Target


No vi ninguna respuesta cruzada, tampoco es necesaria la extracción xml. Aquí hay una versión ligeramente diferente de lo que escribió Kevin Fairchild. Es más rápido y más fácil de usar en consultas más complejas:

select T.ID ,MAX(X.cl) NameValues from #YourTable T CROSS APPLY (select STUFF(( SELECT '', '' + [Name] + '':'' + CAST([Value] AS VARCHAR(MAX)) FROM #YourTable WHERE (ID = T.ID) FOR XML PATH('''')) ,1,2,'''') [cl]) X GROUP BY T.ID


Ocho años después ... Microsoft SQL Server vNext Database Engine finalmente ha mejorado Transact-SQL para admitir directamente la concatenación de cadenas agrupadas. La versión 1.0 de Community Technical Preview agregó la función STRING_AGG y CTP 1.1 agregó la cláusula WITHIN GROUP para la función STRING_AGG.

Referencia: https://msdn.microsoft.com/en-us/library/mt775028.aspx


Otra opción usando Sql Server 2005 y superior

---- 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


Puede mejorar significativamente el rendimiento de la siguiente manera si agrupar por contiene principalmente un elemento:

SELECT [ID], CASE WHEN MAX( [Name]) = MIN( [Name]) THEN MAX( [Name]) NameValues ELSE 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 END FROM #YourTable Results GROUP BY ID


SQL Server 2005 y versiones posteriores le permiten crear sus propias funciones agregadas personalizadas , incluso para cosas como la concatenación: vea la muestra al final del artículo vinculado.


Seamos muy simples:

SELECT stuff( ( select '', '' + x from (SELECT ''xxx'' x union select ''yyyy'') tb FOR XML PATH('''') ) , 1, 2, '''')

Reemplace esta línea:

select '', '' + x from (SELECT ''xxx'' x union select ''yyyy'') tb

Con su consulta.


Si es SQL Server 2017 o SQL Server Vnext, SQL Azure puede usar string_agg como se muestra a continuación:

select id, string_agg(concat(name, '':'', [value]), '', '') from #YourTable group by id


Solo para agregar a lo que dijo Cade, esto suele ser una cosa de la pantalla frontal y, por lo tanto, debe manejarse allí. Sé que a veces es más fácil escribir algo al 100% en SQL para cosas como la exportación de archivos u otras soluciones "solo para SQL", pero la mayoría de las veces esta concatenación debe manejarse en la capa de visualización.


el uso de la ruta XML no concatenará perfectamente como podría esperarse ... reemplazará "&" con "& amp;" y también se mezclará con <" and "> ... quizás algunas otras cosas, no estoy seguro ... pero puedes intentar esto

Me encontré con una solución para esto ... necesita reemplazar:

FOR XML PATH('''') )

con:

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

... o NVARCHAR(MAX) si eso es lo que estás usando.

¿Por qué demonios no tiene SQL una función agregada de concatenación? esto es un PITA.


No se necesita CURSOR, bucle WHILE o función definida por el usuario .

Solo necesitas ser creativo con FOR XML y PATH.

[Nota: esta solución solo funciona en SQL 2005 y versiones posteriores. La pregunta original no especificó la versión en uso.]

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