¿Cómo concatenar texto de varias filas en una sola cadena de texto en el servidor SQL?

1913

Considere una tabla de base de datos con nombres, con tres filas:

Peter
Paul
Mary

¿Hay una manera fácil de convertir esto en una sola cadena de Peter, Paul, Mary?

JohnnyM
fuente
26
Para obtener respuestas específicas a SQL Server, intente esta pregunta .
Matt Hamilton
17
Para MySQL, consulte Group_Concat de esta respuesta
Pykler,
26
Desearía que la próxima versión de SQL Server ofreciera una nueva característica para resolver la concatenación de cadenas de varias filas de manera elegante sin la tontería de FOR XML PATH.
Pete Alvin el
44
No es SQL, pero si se trata de una sola vez, puede pegar la lista en esta herramienta del navegador convert.town/column-to-comma-separated-list
Stack Man
3
En Oracle, puede usar el LISTAGG (COLUMN_NAME) de 11g r2 antes de que haya una función no compatible llamada WM_CONCAT (COLUMN_NAME) que hace lo mismo.
Richard

Respuestas:

1432

Si está utilizando SQL Server 2017 o Azure, consulte la respuesta de Mathieu Renda .

Tuve un problema similar cuando intentaba unir dos tablas con relaciones uno a muchos. En SQL 2005 descubrí que el XML PATHmétodo puede manejar la concatenación de las filas muy fácilmente.

Si hay una tabla llamada STUDENTS

SubjectID       StudentName
----------      -------------
1               Mary
1               John
1               Sam
2               Alaina
2               Edward

El resultado que esperaba era:

SubjectID       StudentName
----------      -------------
1               Mary, John, Sam
2               Alaina, Edward

Usé lo siguiente T-SQL:

SELECT Main.SubjectID,
       LEFT(Main.Students,Len(Main.Students)-1) As "Students"
FROM
    (
        SELECT DISTINCT ST2.SubjectID, 
            (
                SELECT ST1.StudentName + ',' AS [text()]
                FROM dbo.Students ST1
                WHERE ST1.SubjectID = ST2.SubjectID
                ORDER BY ST1.SubjectID
                FOR XML PATH ('')
            ) [Students]
        FROM dbo.Students ST2
    ) [Main]

Puede hacer lo mismo de una manera más compacta si puede concatenar las comas al principio y usar substringpara omitir la primera, por lo que no necesita hacer una subconsulta:

SELECT DISTINCT ST2.SubjectID, 
    SUBSTRING(
        (
            SELECT ','+ST1.StudentName  AS [text()]
            FROM dbo.Students ST1
            WHERE ST1.SubjectID = ST2.SubjectID
            ORDER BY ST1.SubjectID
            FOR XML PATH ('')
        ), 2, 1000) [Students]
FROM dbo.Students ST2
StefanJCollier
fuente
13
Gran solución Lo siguiente puede ser útil si necesita manejar caracteres especiales como los de HTML: Rob Farley: Manejo de caracteres especiales con FOR XML PATH ('') .
10
Aparentemente, esto no funciona si los nombres contienen caracteres XML como <o &. Ver el comentario de @ BenHinman.
Sam
23
NB: este método depende del comportamiento indocumentado de FOR XML PATH (''). Eso significa que no debe considerarse confiable ya que cualquier parche o actualización podría alterar cómo funciona esto. Básicamente se basa en una característica obsoleta.
Bacon Bits
26
@Whelkaholism La conclusión es que FOR XMLestá destinada a generar XML, no concatenar cadenas arbitrarias. Es por eso que se escapa &, <y >los códigos de entidad XML ( &amp;, &lt;, &gt;). Supongo que también escapará "y 'hacia &quot;y &apos;en los atributos también. Está no GROUP_CONCAT() , string_agg(), array_agg(), listagg(), etc, incluso si usted puede tipo de maquillaje que hacer eso. Nosotros deberíamos estar gastando nuestro tiempo exigiendo Microsoft implementar una función adecuada.
Bacon Bits
13
Buenas noticias: MS SQL Server se agregará string_aggen v.Next. Y todo esto puede desaparecer.
Jason C
1009

Esta respuesta puede devolver resultados inesperados Para obtener resultados consistentes, use uno de los métodos FOR XML PATH detallados en otras respuestas.

Uso COALESCE:

DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(@Names + ', ', '') + Name 
FROM People

Solo una explicación (ya que esta respuesta parece obtener vistas relativamente regulares):

  • Coalesce es realmente solo un truco útil que logra dos cosas:

1) No es necesario inicializar @Namescon un valor de cadena vacío.

2) No es necesario quitar un separador adicional al final.

  • La solución anterior dará resultados incorrectos si una fila tiene un valor de Nombre NULL (si hay un NULL , el NULL hará @Names NULL después de esa fila, y la siguiente fila comenzará de nuevo como una cadena vacía de nuevo. Se soluciona fácilmente con uno de dos soluciones:
DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
WHERE Name IS NOT NULL

o:

DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(@Names + ', ', '') + 
    ISNULL(Name, 'N/A')
FROM People

Dependiendo del comportamiento que desee (la primera opción solo filtra los NULL , la segunda opción los mantiene en la lista con un mensaje marcador [reemplace 'N / A' con lo que sea apropiado para usted]).

Chris Shaffer
fuente
72
Para ser claros, la fusión no tiene nada que ver con la creación de la lista, solo se asegura de que los valores NULL no estén incluidos.
Graeme Perrow
17
@Graeme Perrow No excluye los valores NULL (se necesita un WHERE para eso; esto perderá resultados si uno de los valores de entrada es NULL), y se requiere en este enfoque porque: NULL + non-NULL -> NULL y no NULL + NULL -> NULL; también @Name es NULL de forma predeterminada y, de hecho, esa propiedad se usa aquí como un centinela implícito para determinar si se debe agregar o no un ','.
62
Tenga en cuenta que este método de concatenación se basa en que SQL Server ejecute la consulta con un plan particular. Me han pillado usando este método (con la adición de un ORDER BY). Cuando se trataba de un pequeño número de filas, funcionó bien, pero con más datos, SQL Server eligió un plan diferente que resultó en la selección del primer elemento sin concatenación alguna. Ver este artículo de Anith Sen.
fbarber
17
Este método no puede usarse como una subconsulta en una lista de selección o cláusula where, porque usa una variable tSQL. En tales casos, podría usar los métodos ofrecidos por @Ritesh
R. Schreurs el
11
Este no es un método confiable de concatenación. No es compatible y no debe usarse (según Microsoft, por ejemplo , support.microsoft.com/en-us/kb/287515 , connect.microsoft.com/SQLServer/Feedback/Details/704389 ). Puede cambiar sin previo aviso. Utilice la técnica XML PATH que se describe en stackoverflow.com/questions/5031204/… . Escribí más aquí: marc.durdin.net/2015/07/…
Marc Durdin
454

SQL Server 2017+ y SQL Azure: STRING_AGG

Comenzando con la próxima versión de SQL Server, finalmente podemos concatenar entre filas sin tener que recurrir a ninguna brujería variable o XML.

STRING_AGG (Transact-SQL)

Sin agrupar

SELECT STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department;

Con agrupación:

SELECT GroupName, STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department
GROUP BY GroupName;

Con agrupación y subclasificación

SELECT GroupName, STRING_AGG(Name, ', ') WITHIN GROUP (ORDER BY Name ASC) AS Departments
FROM HumanResources.Department 
GROUP BY GroupName;
Mathieu Renda
fuente
2
Y, a diferencia de las soluciones CLR, usted tiene control sobre la clasificación.
canon
Parece que hay una limitación de visualización de 4000 caracteres en STRING_AGG
InspiredBy
¿Hay alguna forma de ordenar en caso de que no haya GROUP BY (por ejemplo, para el ejemplo "Sin agrupar")?
RuudvK
Actualización: logré hacer lo siguiente, pero ¿hay una forma más limpia? SELECT STRING_AGG (Nombre, ',') AS Departamentos FROM (SELECCIONE TOP 100000 Name FROM HumanResources.Department ORDER BY Name) D;
RuudvK
362

Un método que aún no se muestra a través del XML data()comando en MS SQL Server es:

Suponga que la tabla llamada NameList con una columna llamada FName,

SELECT FName + ', ' AS 'data()' 
FROM NameList 
FOR XML PATH('')

devoluciones:

"Peter, Paul, Mary, "

Solo se debe tratar la coma extra.

Editar: tal como se adoptó del comentario de @ NReilingh, puede usar el siguiente método para eliminar la coma final. Asumiendo los mismos nombres de tabla y columna:

STUFF(REPLACE((SELECT '#!' + LTRIM(RTRIM(FName)) AS 'data()' FROM NameList
FOR XML PATH('')),' #!',', '), 1, 2, '') as Brands
jens frandsen
fuente
15
¡Santo cielo, eso es asombroso! Cuando se ejecuta solo, como en su ejemplo, el resultado se formatea como un hipervínculo, que cuando se hace clic (en SSMS) abre una nueva ventana que contiene los datos, pero cuando se usa como parte de una consulta más grande, solo aparece como una cadena. ¿Es una cuerda? ¿o es xml lo que necesito tratar de manera diferente en la aplicación que utilizará estos datos?
Ben
10
Este enfoque también escapa caracteres XML como <y>. Entonces, SELECCIONAR '<b>' + FName + '</b>' da como resultado "& lt; b & gt; John & lt; / b & gt; & lt; b & gt; Paul ..."
Lukáš Lánský
8
Solución ordenada Me doy cuenta de que incluso cuando no agrego el + ', ', todavía agrega un solo espacio entre cada elemento concatenado.
Baodad
8
@Baodad Eso parece ser parte del trato. Puede solucionarlo reemplazando un carácter de token agregado. Por ejemplo, esta es una lista perfecta delimitada por comas para cualquier longitud:SELECT STUFF(REPLACE((SELECT '#!'+city AS 'data()' FROM #cityzip FOR XML PATH ('')),' #!',', '),1,2,'')
NReilingh
1
Wow, en realidad en mis pruebas usando data () y un reemplazo es MUCHO más eficiente que no. Súper raro
NReilingh
306

En SQL Server 2005

SELECT Stuff(
  (SELECT N', ' + Name FROM Names FOR XML PATH(''),TYPE)
  .value('text()[1]','nvarchar(max)'),1,2,N'')

En SQL Server 2016

puedes usar la sintaxis FOR JSON

es decir

SELECT per.ID,
Emails = JSON_VALUE(
   REPLACE(
     (SELECT _ = em.Email FROM Email em WHERE em.Person = per.ID FOR JSON PATH)
    ,'"},{"_":"',', '),'$[0]._'
) 
FROM Person per

Y el resultado se convertirá

Id  Emails
1   abc@gmail.com
2   NULL
3   def@gmail.com, xyz@gmail.com

Esto funcionará incluso si sus datos contienen caracteres XML no válidos

el '"},{"_":"'es seguro porque si contenerte datos '"},{"_":"',que se escapó a"},{\"_\":\"

Se puede reemplazar ', 'con cualquier separador de cadenas


Y en SQL Server 2017, Azure SQL Database

Puedes usar la nueva función STRING_AGG

Steven Chong
fuente
3
Buen uso de la función STUFF para rechazar los dos caracteres principales.
David
3
Esta solución me gusta más, porque puedo usarla fácilmente en una lista de selección agregando 'como <etiqueta>'. No estoy seguro de cómo hacer esto con la solución de @Ritesh.
R. Schreurs
13
Esto es mejor que la respuesta aceptada ya que esta opción también se encarga de caracteres XML reservados ONU-escape tales como <, >, &, etc, que FOR XML PATH('')se escapará automáticamente.
BateTech
Esta es una respuesta increíble, ya que resolvió el problema y proporciona las mejores formas de hacer las cosas en diferentes versiones de SQL. Ahora desearía poder usar 2017 / Azure
Chris Ward
120

En MySQL hay una función, GROUP_CONCAT () , que le permite concatenar los valores de varias filas. Ejemplo:

SELECT 1 AS a, GROUP_CONCAT(name ORDER BY name ASC SEPARATOR ', ') AS people 
FROM users 
WHERE id IN (1,2,3) 
GROUP BY a
Darryl Hein
fuente
funciona bien. Pero cuando lo use SEPARATOR '", "', extrañaré algunos caracteres al final de la última entrada. ¿Por qué puede suceder esto?
gooleem
@gooleem No tengo claro qué quieres decir, pero esta función solo coloca el separador entre los elementos, no después. Si esa no es la respuesta, recomendaría publicar una nueva pregunta.
Darryl Hein
@DarrylHein para mis necesidades, utilicé el separador como arriba. Pero esto me corta algunos caracteres al final de la salida. Esto es muy extraño y parece ser un error. No tengo una solución, simplemente trabajé alrededor.
gooleem
Funciona básicamente. Dos cosas a tener en cuenta: 1) si su columna no es a CHAR, debe GROUP_CONCAT( CAST(id AS CHAR(8)) ORDER BY id ASC SEPARATOR ',')group_concat_max_len
emitirla
58

Use COALESCE - Obtenga más información desde aquí

Para un ejemplo:

102

103

104

Luego escriba el siguiente código en el servidor SQL,

Declare @Numbers AS Nvarchar(MAX) -- It must not be MAX if you have few numbers 
SELECT  @Numbers = COALESCE(@Numbers + ',', '') + Number
FROM   TableName where Number IS NOT NULL

SELECT @Numbers

La salida sería:

102,103,104
pedram
fuente
2
Esta es realmente la mejor solución IMO ya que evita los problemas de codificación que presenta FOR XML. Solía Declare @Numbers AS Nvarchar(MAX)y funcionó bien. ¿Puede explicar por qué recomienda no usarlo, por favor?
EvilDr
77
¡Esta solución ya se publicó hace 8 años! stackoverflow.com/a/194887/986862
Andre Figueiredo
¿Por qué se devuelve esta consulta? símbolos en lugar de cirílicos? ¿Es esto solo un problema de salida?
Akmal Salikhov
48

Las matrices de Postgres son increíbles. Ejemplo:

Crea algunos datos de prueba:

postgres=# \c test
You are now connected to database "test" as user "hgimenez".
test=# create table names (name text);
CREATE TABLE                                      
test=# insert into names (name) values ('Peter'), ('Paul'), ('Mary');                                                          
INSERT 0 3
test=# select * from names;
 name  
-------
 Peter
 Paul
 Mary
(3 rows)

Agréguelos en una matriz:

test=# select array_agg(name) from names;
 array_agg     
------------------- 
 {Peter,Paul,Mary}
(1 row)

Convierta la matriz en una cadena delimitada por comas:

test=# select array_to_string(array_agg(name), ', ') from names;
 array_to_string
-------------------
 Peter, Paul, Mary
(1 row)

HECHO

Desde PostgreSQL 9.0 es aún más fácil .

hgmnz
fuente
Si necesita más de una columna, por ejemplo, su identificación de empleado entre paréntesis use el operador concat: select array_to_string(array_agg(name||'('||id||')'
Richard Fox
No aplicable a sql-server , solo a mysql
GoldBishop
46

Oracle 11g Release 2 es compatible con la función LISTAGG. Documentación aquí .

COLUMN employees FORMAT A50

SELECT deptno, LISTAGG(ename, ',') WITHIN GROUP (ORDER BY ename) AS employees
FROM   emp
GROUP BY deptno;

    DEPTNO EMPLOYEES
---------- --------------------------------------------------
        10 CLARK,KING,MILLER
        20 ADAMS,FORD,JONES,SCOTT,SMITH
        30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

3 rows selected.

Advertencia

Tenga cuidado al implementar esta función si existe la posibilidad de que la cadena resultante supere los 4000 caracteres. Lanzará una excepción. Si ese es el caso, debe manejar la excepción o rodar su propia función que evita que la cadena unida supere los 4000 caracteres.

Alex
fuente
1
Para versiones anteriores de Oracle, wm_concat es perfecto. Su uso se explica en el enlace regalo de Alex. Gracias Alex!
toscanelli
LISTAGGfunciona perfecto! Solo lea el documento vinculado aquí. wm_concateliminado de la versión 12c en adelante.
pregunta el
34

En SQL Server 2005 y versiones posteriores, use la consulta a continuación para concatenar las filas.

DECLARE @t table
(
    Id int,
    Name varchar(10)
)
INSERT INTO @t
SELECT 1,'a' UNION ALL
SELECT 1,'b' UNION ALL
SELECT 2,'c' UNION ALL
SELECT 2,'d' 

SELECT ID,
stuff(
(
    SELECT ','+ [Name] FROM @t WHERE Id = t.Id FOR XML PATH('')
),1,1,'') 
FROM (SELECT DISTINCT ID FROM @t ) t
Yogesh Bhadauirya
fuente
2
Creo que esto falla cuando los valores contienen símbolos XML como <o &.
Sam
28

No tengo acceso a un servidor SQL en casa, así que supongo que la sintaxis aquí, pero es más o menos:

DECLARE @names VARCHAR(500)

SELECT @names = @names + ' ' + Name
FROM Names
Dana
fuente
11
Debería iniciar @nombres en algo no nulo, de lo contrario obtendrá NULL en todo momento; también necesitaría manejar el delimitador (incluido el innecesario)
Marc Gravell
3
El único problema con este enfoque (que uso todo el tiempo) es que no se puede incrustar
ekkis
1
Para deshacerse del espacio inicial, cambie la consulta aSELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ' ' END + Name FROM Names
Tian van Heerden
Además, debe verificar que el Nombre no sea nulo, puede hacerlo haciendo:SELECT @names = @names + ISNULL(' ' + Name, '')
Vita1ij
28

Se sugirió una solución recursiva de CTE, pero no se proporcionó ningún código. El siguiente código es un ejemplo de un CTE recursivo. Tenga en cuenta que si bien los resultados se ajustan a la pregunta, los datos no bastante coincide con la descripción dada, como supongo que realmente desea estar haciendo esto en grupos de filas, no todas las filas de la tabla. Cambiarlo para que coincida con todas las filas de la tabla se deja como ejercicio para el lector.

;WITH basetable AS (
    SELECT
        id,
        CAST(name AS VARCHAR(MAX)) name, 
        ROW_NUMBER() OVER (Partition BY id ORDER BY seq) rw, 
        COUNT(*) OVER (Partition BY id) recs 
    FROM (VALUES
        (1, 'Johnny', 1),
        (1, 'M', 2), 
        (2, 'Bill', 1),
        (2, 'S.', 4),
        (2, 'Preston', 5),
        (2, 'Esq.', 6),
        (3, 'Ted', 1),
        (3, 'Theodore', 2),
        (3, 'Logan', 3),
        (4, 'Peter', 1),
        (4, 'Paul', 2),
        (4, 'Mary', 3)
    ) g (id, name, seq)
),
rCTE AS (
    SELECT recs, id, name, rw
    FROM basetable
    WHERE rw = 1

    UNION ALL

    SELECT b.recs, r.ID, r.name +', '+ b.name name, r.rw + 1
    FROM basetable b
    INNER JOIN rCTE r ON b.id = r.id AND b.rw = r.rw + 1
)
SELECT name
FROM rCTE
WHERE recs = rw AND ID=4
jmoreno
fuente
1
Para los asombrados: esta consulta inserta 12 filas (3 columnas) en una tabla base temporal, luego crea una expresión de tabla común recursiva (rCTE) y luego aplana la name columna en una cadena separada por comas para 4 grupos de ids. A primera vista, creo que esto es más trabajo que lo que hacen la mayoría de las otras soluciones para SQL Server.
knb
2
@knb: no estoy seguro si eso es un elogio, una condena o simplemente una sorpresa. La tabla base es porque me gusta que mis ejemplos realmente funcionen, realmente no tiene nada que ver con la pregunta.
jmoreno
26

Comenzando con PostgreSQL 9.0, esto es bastante simple:

select string_agg(name, ',') 
from names;

En versiones anteriores a 9.0 array_agg()se puede usar como se muestra en hgmnz

un caballo sin nombre
fuente
Para hacer esto con columnas que no son de tipo texto, debe agregar un tipo de conversión:SELECT string_agg(non_text_type::text, ',') FROM table
Torben Kohlmeier
@TorbenKohlmeier: solo necesita eso para columnas sin caracteres (por ejemplo, entero, decimal). Funciona bien para varcharochar
a_horse_with_no_name
26

Necesita crear una variable que contendrá su resultado final y seleccionarlo, así.

La solución más fácil

DECLARE @char VARCHAR(MAX);

SELECT @char = COALESCE(@char + ', ' + [column], [column]) 
FROM [table];

PRINT @char;
Tigerjz32
fuente
18

Usar XML me ayudó a separar las filas con comas. Para la coma adicional, podemos usar la función de reemplazo de SQL Server. En lugar de agregar una coma, el uso de AS 'data ()' concatenará las filas con espacios, que luego se pueden reemplazar con comas como la sintaxis escrita a continuación.

REPLACE(
        (select FName AS 'data()'  from NameList  for xml path(''))
         , ' ', ', ') 
Diwakar
fuente
2
Esta es la mejor respuesta aquí en mi opinión. El uso de declarar variable no es bueno cuando necesita unirse a otra tabla, y esto es bueno y breve. Buen trabajo.
David Roussel
77
eso no funciona bien si los datos de FName ya tienen espacios, por ejemplo "Mi nombre"
binball
Realmente está funcionando para mí en ms-sql 2016 Seleccione REPLACE ((seleccione Name AS 'data ()' de Brand Where Id IN (1,2,3,4) para xml path ('')), '', ' , ') como allBrands
Rejwanul Reja
17

Una solución lista para usar, sin comas adicionales:

select substring(
        (select ', '+Name AS 'data()' from Names for xml path(''))
       ,3, 255) as "MyList"

Una lista vacía dará como resultado un valor NULL. Por lo general, insertará la lista en una columna de tabla o variable de programa: ajuste la longitud máxima de 255 a su necesidad.

(Diwakar y Jens Frandsen dieron buenas respuestas, pero necesitan mejorar).

Daniel Reis
fuente
Hay un espacio antes de la coma cuando se usa esto :(
slayernoah
1
Simplemente reemplace ', 'con ','si no desea el espacio extra.
Daniel Reis
13
SELECT STUFF((SELECT ', ' + name FROM [table] FOR XML PATH('')), 1, 2, '')

Aquí hay una muestra:

DECLARE @t TABLE (name VARCHAR(10))
INSERT INTO @t VALUES ('Peter'), ('Paul'), ('Mary')
SELECT STUFF((SELECT ', ' + name FROM @t FOR XML PATH('')), 1, 2, '')
--Peter, Paul, Mary
Max Szczurek
fuente
10
DECLARE @Names VARCHAR(8000)
SELECT @name = ''
SELECT @Names = @Names + ',' + Names FROM People
SELECT SUBSTRING(2, @Names, 7998)

Esto pone la coma perdida al principio.

Sin embargo, si necesita otras columnas o para CSV una tabla secundaria, debe ajustar esto en un campo escalar definido por el usuario (UDF).

También puede usar la ruta XML como una subconsulta correlacionada en la cláusula SELECT (pero tendría que esperar hasta volver a trabajar porque Google no hace cosas de trabajo en casa :-)

gbn
fuente
10

Con las otras respuestas, la persona que lee la respuesta debe conocer una tabla de dominio específica, como el vehículo o el estudiante. La tabla debe crearse y rellenarse con datos para probar una solución.

A continuación se muestra un ejemplo que utiliza la tabla "Information_Schema.Columns" de SQL Server. Al usar esta solución, no es necesario crear tablas ni agregar datos. Este ejemplo crea una lista separada por comas de nombres de columna para todas las tablas en la base de datos.

SELECT
    Table_Name
    ,STUFF((
        SELECT ',' + Column_Name
        FROM INFORMATION_SCHEMA.Columns Columns
        WHERE Tables.Table_Name = Columns.Table_Name
        ORDER BY Column_Name
        FOR XML PATH ('')), 1, 1, ''
    )Columns
FROM INFORMATION_SCHEMA.Columns Tables
GROUP BY TABLE_NAME 
Mike Barlow - BarDev
fuente
7

Para Oracle DBs, vea esta pregunta: ¿Cómo se pueden concatenar varias filas en una en Oracle sin crear un procedimiento almacenado?

La mejor respuesta parece ser @Emmanuel, utilizando la función incorporada LISTAGG (), disponible en Oracle 11g Release 2 y posterior.

SELECT question_id,
   LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM YOUR_TABLE;
GROUP BY question_id

como señaló @ user762952, y de acuerdo con la documentación de Oracle http://www.oracle-base.com/articles/misc/string-aggregation-techniques.php , la función WM_CONCAT () también es una opción. Parece estable, pero Oracle recomienda explícitamente no usarlo para ninguna aplicación SQL, así que úselo bajo su propio riesgo.

Aparte de eso, tendrá que escribir su propia función; El documento de Oracle anterior tiene una guía sobre cómo hacerlo.

ZeroK
fuente
7

Para evitar valores nulos, puede usar CONCAT ()

DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name) 
FROM Names
select @names
Rapunzo
fuente
Sería bueno saber por qué funciona CONCAT. Un enlace a MSDN estaría bien.
Ingeniero invertido
7

Realmente me gustó la elegancia de la respuesta de Dana . Solo quería completarlo.

DECLARE @names VARCHAR(MAX)
SET @names = ''

SELECT @names = @names + ', ' + Name FROM Names 

-- Deleting last two symbols (', ')
SET @sSql = LEFT(@sSql, LEN(@sSql) - 1)
Oleg Sakharov
fuente
Si está eliminando los dos últimos símbolos ',', debe agregar ',' después de Nombre ('SELECCIONAR \ @nombres = \ @nombres + Nombre +', 'DE Nombres'). De esa manera, los dos últimos caracteres siempre serán ','.
JT_
En mi caso necesitaba para deshacerse del líder en coma por lo que cambiar la consulta a SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ', ' END + Name FROM Namescontinuación, usted no tiene que truncar después.
Tian van Heerden
6

Esta respuesta requerirá algún privilegio en el servidor para funcionar.

Las asambleas son una buena opción para ti. Hay muchos sitios que explican cómo crearlo. El que yo creo que está muy bien explicado es este uno

Si lo desea, ya he creado el ensamblaje, y es posible descargar la DLL aquí .

Una vez que lo haya descargado, deberá ejecutar el siguiente script en su SQL Server:

CREATE Assembly concat_assembly 
   AUTHORIZATION dbo 
   FROM '<PATH TO Concat.dll IN SERVER>' 
   WITH PERMISSION_SET = SAFE; 
GO 

CREATE AGGREGATE dbo.concat ( 

    @Value NVARCHAR(MAX) 
  , @Delimiter NVARCHAR(4000) 

) RETURNS NVARCHAR(MAX) 
EXTERNAL Name concat_assembly.[Concat.Concat]; 
GO  

sp_configure 'clr enabled', 1;
RECONFIGURE

Observe que la ruta al ensamblaje puede ser accesible para el servidor. Como ha realizado con éxito todos los pasos, puede usar la función como:

SELECT dbo.Concat(field1, ',')
FROM Table1

¡¡¡Espero eso ayude!!!

ejército turco
fuente
1
El enlace DLL es un error 404. Usar un ensamblaje para esto es exagerado. Vea la mejor respuesta para SQL Server.
El
6

Ejemplo completo de MySQL:

Tenemos usuarios que pueden tener muchos datos y queremos tener una salida, donde podamos ver todos los datos de los usuarios en una lista:

Resultado:

___________________________
| id   |  rowList         |
|-------------------------|
| 0    | 6, 9             |
| 1    | 1,2,3,4,5,7,8,1  |
|_________________________|

Configuración de tabla:

CREATE TABLE `Data` (
  `id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1;


INSERT INTO `Data` (`id`, `user_id`) VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(5, 1),
(6, 0),
(7, 1),
(8, 1),
(9, 0),
(10, 1);


CREATE TABLE `User` (
  `id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


INSERT INTO `User` (`id`) VALUES
(0),
(1);

Consulta:

SELECT User.id, GROUP_CONCAT(Data.id ORDER BY Data.id) AS rowList FROM User LEFT JOIN Data ON User.id = Data.user_id GROUP BY User.id
usuario1767754
fuente
5

Usualmente uso select como este para concatenar cadenas en SQL Server:

with lines as 
( 
  select 
    row_number() over(order by id) id, -- id is a line id
    line -- line of text.
  from
    source -- line source
), 
result_lines as 
( 
  select 
    id, 
    cast(line as nvarchar(max)) line 
  from 
    lines 
  where 
    id = 1 
  union all 
  select 
    l.id, 
    cast(r.line + N', ' + l.line as nvarchar(max))
  from 
    lines l 
    inner join 
    result_lines r 
    on 
      l.id = r.id + 1 
) 
select top 1 
  line
from
  result_lines
order by
  id desc
Vladimir Nesterovsky
fuente
5

Si desea tratar con valores nulos, puede hacerlo agregando una cláusula where o agregando otra COALESCE alrededor de la primera.

DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(COALESCE(@Names + ', ', '') + Name, @Names) FROM People
Pramod
fuente
5

Esto funcionó para mí ( SqlServer 2016 ):

SELECT CarNamesString = STUFF((
         SELECT ',' + [Name]
            FROM tbl_cars 
            FOR XML PATH('')
         ), 1, 1, '')

Aquí está la fuente: https://www.mytecbits.com/

Y una solución para MySql (ya que esta página aparece en Google para MySql)

SELECT [Name],
       GROUP_CONCAT(DISTINCT [Name]  SEPARATOR ',')
       FROM tbl_cars

De documentaciones MySql

Arash.Zandi
fuente
4

En Oracle, lo es wm_concat. Creo que esta función está disponible en la versión 10g y superior.

usuario762952
fuente
4

Esto también puede ser útil

create table #test (id int,name varchar(10))
--use separate inserts on older versions of SQL Server
insert into #test values (1,'Peter'), (1,'Paul'), (1,'Mary'), (2,'Alex'), (3,'Jack')

DECLARE @t VARCHAR(255)
SELECT @t = ISNULL(@t + ',' + name, name) FROM #test WHERE id = 1
select @t
drop table #test

devoluciones

Peter,Paul,Mary
endo64
fuente
55
Lamentablemente, este comportamiento no parece ser oficialmente compatible. MSDN dice: "Si se hace referencia a una variable en una lista de selección, se le debe asignar un valor escalar o la instrucción SELECT solo debe devolver una fila". Y hay personas que observaron problemas: sqlmag.com/sql-server/multi-row-variable-assignment-and-order
blueling