String_agg para SQL Server antes de 2017

Resuelto xMilos asked hace 6 años • 2 respuestas

¿Alguien puede ayudarme a hacer que esta consulta funcione para SQL Server 2014?

Esto funciona en Postgresql y probablemente en SQL Server 2017. En Oracle es listaggen lugar de string_agg.

Aquí está el SQL:

select 
    string_agg(t.id,',') AS id
from 
    Table t

Revisé en el sitio que se debería usar alguna opción xml pero no pude entenderla.

xMilos avatar Mar 19 '18 17:03 xMilos
Aceptado

En SQL Server anterior a 2017, puede hacer:

select stuff( (select ',' + cast(t.id as varchar(max))
               from tabel t
               for xml path ('')
              ), 1, 1, ''
            );

El único propósito de stuff()es eliminar la coma inicial. El trabajo lo está realizando for xml path.

Gordon Linoff avatar Mar 19 '2018 10:03 Gordon Linoff

Tenga en cuenta que para algunos caracteres, los valores se escaparán cuando se utilice FOR XML PATH, por ejemplo:

SELECT STUFF((SELECT ',' + V.String
              FROM (VALUES('7 > 5'),('Salt & pepper'),('2
lines'))V(String)
              FOR XML PATH('')),1,1,'');

Esto devuelve la siguiente cadena:

7 > 5,Salt & pepper,2
lines'

Es poco probable que esto sea deseado. Puedes solucionar esto usando TYPEy luego obteniendo el valor del XML:

SELECT STUFF((SELECT ',' + V.String
              FROM (VALUES('7 > 5'),('Salt & pepper'),('2
lines'))V(String)
              FOR XML PATH(''),TYPE).value('(./text())[1]','varchar(MAX)'),1,1,'');

Esto devuelve la siguiente cadena:

7 > 5,Salt & pepper,2
lines

Esto replicaría el comportamiento de lo siguiente:

SELECT STRING_AGG(V.String,',')
FROM VALUES('7 > 5'),('Salt & pepper'),('2
lines'))V(String);

Por supuesto, puede haber ocasiones en las que desee agrupar los datos, lo que lo anterior no demuestra. Para lograr esto, necesitaría utilizar una subconsulta correlacionada. Tome los siguientes datos de muestra:

CREATE TABLE dbo.MyTable (ID int IDENTITY(1,1),
                          GroupID int,
                          SomeCharacter char(1));

INSERT INTO dbo.MyTable (GroupID, SomeCharacter)
VALUES (1,'A'), (1,'B'), (1,'D'),
       (2,'C'), (2,NULL), (2,'Z');

De esto queríamos los siguientes resultados:

Identificación del grupo Caracteres
1 A,B,D
2 C,Z

Para lograr esto necesitarías hacer algo como esto:

SELECT MT.GroupID,
       STUFF((SELECT ',' + sq.SomeCharacter 
              FROM dbo.MyTable sq
              WHERE sq.GroupID = MT.GroupID --This is your correlated join and should be on the same columns as your GROUP BY
                                            --You "JOIN" on the columns that would have been in the PARTITION BY
              FOR XML PATH(''),TYPE).value('(./text())[1]','varchar(MAX)'),1,1,'')
FROM dbo.MyTable MT
GROUP BY MT.GroupID; --I use GROUP BY rather than DISTINCT as we are technically aggregating here

Entonces, si estuviera agrupando en 2 columnas, entonces tendría 2 cláusulas en su subconsulta WHERE: WHERE MT.SomeColumn = sq.SomeColumn AND MT.AnotherColumn = sq.AnotherColumny su exterior GROUP BYsería GROUP BY MT.SomeColumn, MT.AnotherColumn.


Finalmente, agreguemos un ORDER BYa esto, que también define en la subconsulta. Supongamos, por ejemplo, que desea ordenar los datos por el valor descendente IDen la agregación de cadenas:

SELECT MT.GroupID,
       STUFF((SELECT ',' + sq.SomeCharacter 
              FROM dbo.MyTable sq
              WHERE sq.GroupID = MT.GroupID
              ORDER BY sq.ID DESC --This is identical to the ORDER BY you would have in your OVER clause
              FOR XML PATH(''),TYPE).value('(./text())[1]','varchar(MAX)'),1,1,'')
FROM dbo.MyTable MT
GROUP BY MT.GroupID;

Para produciría los siguientes resultados:

Identificación del grupo Caracteres
1 D,B,A
2 Z,C

Como era de esperar, esto nunca será tan eficiente como STRING_AGG, debido a que la tabla hace referencia varias veces (si necesita realizar múltiples agregaciones, entonces necesita múltiples subconsultas), pero una tabla bien indexada será de gran ayuda para el RDBMS. Si el rendimiento realmente es un problema, porque está haciendo múltiples agregaciones de cadenas en una sola consulta, entonces le sugeriría que reconsidere si necesita la agregación, o ya es hora de que considere actualizar.

Thom A avatar Oct 06 '2020 14:10 Thom A