Comment concaténer du texte de plusieurs lignes en une seule chaîne de texte dans SQL Server?

1913

Considérons une table de base de données contenant des noms, avec trois lignes:

Peter
Paul
Mary

Existe-t-il un moyen facile de transformer cela en une seule chaîne de Peter, Paul, Mary?

JohnnyM
la source
26
Pour des réponses spécifiques à SQL Server, essayez cette question .
Matt Hamilton
17
Pour MySQL, consultez Group_Concat de cette réponse
Pykler
26
Je souhaite que la prochaine version de SQL Server offre une nouvelle fonctionnalité pour résoudre la concatination de chaînes multi-lignes avec élégance sans la bêtise de FOR XML PATH.
Pete Alvin
4
Pas SQL, mais si c'est une seule fois, vous pouvez coller la liste dans cet outil dans le navigateur convert.town/column-to-comma-separated-list
Stack Man
3
Dans Oracle, vous pouvez utiliser la LISTE (COLUMN_NAME) de 11g r2 avant qu'il existe une fonction non prise en charge appelée WM_CONCAT (COLUMN_NAME) qui fait de même.
Richard

Réponses:

1432

Si vous êtes sur SQL Server 2017 ou Azure, consultez la réponse de Mathieu Renda .

J'ai rencontré un problème similaire lorsque j'essayais de joindre deux tables avec des relations un à plusieurs. Dans SQL 2005, j'ai trouvé que cette XML PATHméthode peut gérer la concaténation des lignes très facilement.

S'il y a une table appelée STUDENTS

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

Le résultat que j'attendais était:

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

J'ai utilisé ce qui suit 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]

Vous pouvez faire la même chose de manière plus compacte si vous pouvez concaténer les virgules au début et utiliser substringpour ignorer la première afin que vous n'ayez pas besoin de faire une sous-requête:

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
la source
13
Excellente solution. Les éléments suivants peuvent être utiles si vous devez gérer des caractères spéciaux comme ceux en HTML: Rob Farley: Gestion des caractères spéciaux avec FOR XML PATH ('') .
10
Apparemment, cela ne fonctionne pas si les noms contiennent des caractères XML tels que <ou &. Voir le commentaire de @ BenHinman.
Sam
23
NB: Cette méthode dépend du comportement non documenté de FOR XML PATH (''). Cela signifie qu'il ne doit pas être considéré comme fiable, car tout correctif ou mise à jour pourrait altérer son fonctionnement. Cela repose essentiellement sur une fonctionnalité obsolète.
Bacon Bits
26
@Whelkaholism L'essentiel FOR XMLest de générer du XML, pas de concaténer des chaînes arbitraires. Voilà pourquoi il échappe &, <et >aux codes d'entité XML ( &amp;, &lt;, &gt;). Je suppose que ce sera aussi échapper "et 'à &quot;et &apos;dans les attributs aussi bien. Il est pas GROUP_CONCAT() , string_agg(), array_agg(), listagg(), etc. , même si vous pouvez faire ce genre de le faire. Nous devrions passer notre temps à demander à Microsoft de mettre en place une fonction appropriée.
Bacon Bits
13
Bonne nouvelle: MS SQL Server ajoutera string_aggdans v.Next. et tout cela peut disparaître.
Jason C
1009

Cette réponse peut renvoyer des résultats inattendus. Pour des résultats cohérents, utilisez l'une des méthodes FOR XML PATH détaillées dans d'autres réponses.

Utilisation COALESCE:

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

Juste quelques explications (puisque cette réponse semble obtenir des vues relativement régulières):

  • Coalesce est vraiment juste une triche utile qui accomplit deux choses:

1) Pas besoin d'initialiser @Namesavec une valeur de chaîne vide.

2) Pas besoin de retirer un séparateur supplémentaire à la fin.

  • La solution ci-dessus donnera des résultats incorrects si une ligne a une valeur de nom NULL (s'il y a un NULL , le NULL fera @Names NULL après cette ligne, et la ligne suivante recommencera comme une chaîne vide. Facilement corrigé avec l'un des deux solutions:
DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
WHERE Name IS NOT NULL

ou:

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

Selon le comportement que vous souhaitez (la première option filtre uniquement les valeurs NULL , la seconde les conserve dans la liste avec un message marqueur [remplacez «N / A» par ce qui vous convient]).

Chris Shaffer
la source
72
Pour être clair, coalesce n'a rien à voir avec la création de la liste, il s'assure simplement que les valeurs NULL ne sont pas incluses.
Graeme Perrow
17
@Graeme Perrow Il n'exclut pas les valeurs NULL (un WHERE est requis pour cela - cela perdra les résultats si l'une des valeurs d'entrée est NULL), et il est requis dans cette approche car: NULL + non-NULL -> NULL et non NULL + NULL -> NULL; @Name est également NULL par défaut et, en fait, cette propriété est utilisée ici comme sentinelle implicite pour déterminer si un «,» doit être ajouté ou non.
62
Veuillez noter que cette méthode de concaténation repose sur l'exécution par SQL Server de la requête avec un plan particulier. J'ai été surpris en utilisant cette méthode (avec l'ajout d'un ORDER BY). Lorsqu'il s'agissait d'un petit nombre de lignes, cela fonctionnait bien, mais avec plus de données, SQL Server a choisi un plan différent, ce qui a entraîné la sélection du premier élément sans aucune concaténation. Voir cet article d'Anith Sen.
fbarber
17
Cette méthode ne peut pas être utilisée comme sous-requête dans une liste de sélection ou une clause where, car elle utilise une variable tSQL. Dans de tels cas, vous pouvez utiliser les méthodes proposées par @Ritesh
R. Schreurs
11
Ce n'est pas une méthode fiable de concaténation. Il n'est pas pris en charge et ne doit pas être utilisé (par Microsoft, par exemple support.microsoft.com/en-us/kb/287515 , connect.microsoft.com/SQLServer/Feedback/Details/704389 ). Il peut changer sans avertissement. Utilisez la technique XML PATH discutée dans stackoverflow.com/questions/5031204/… J'en ai écrit plus ici: marc.durdin.net/2015/07/…
Marc Durdin
454

SQL Server 2017+ et SQL Azure: STRING_AGG

À partir de la prochaine version de SQL Server, nous pouvons enfin concaténer sur plusieurs lignes sans avoir à recourir à une variable ou à une sorcellerie XML.

STRING_AGG (Transact-SQL)

Sans regroupement

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

Avec regroupement:

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

Avec regroupement et sous-tri

SELECT GroupName, STRING_AGG(Name, ', ') WITHIN GROUP (ORDER BY Name ASC) AS Departments
FROM HumanResources.Department 
GROUP BY GroupName;
Mathieu Renda
la source
2
Et, contrairement aux solutions CLR, vous contrôlez le tri.
canon
Il semble y avoir une limitation d'affichage de 4000 caractères sur STRING_AGG
InspiredBy
Existe-t-il un moyen de trier s'il n'y a pas de GROUP BY (donc pour l'exemple "Sans groupement")?
RuudvK
Mise à jour: j'ai réussi à faire ce qui suit, mais existe-t-il un moyen plus propre? SELECT STRING_AGG (Name, ',') AS Departments FROM (SELECT TOP 100000 Name FROM HumanResources.Department ORDER BY Name) D;
RuudvK
362

Une méthode non encore montrée via la XML data()commande dans MS SQL Server est:

Supposons une table appelée NameList avec une colonne appelée FName,

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

Retour:

"Peter, Paul, Mary, "

Seule la virgule supplémentaire doit être traitée.

Modifier: Comme adopté à partir du commentaire de @ NReilingh, vous pouvez utiliser la méthode suivante pour supprimer la virgule de fin. En supposant les mêmes noms de table et de colonne:

STUFF(REPLACE((SELECT '#!' + LTRIM(RTRIM(FName)) AS 'data()' FROM NameList
FOR XML PATH('')),' #!',', '), 1, 2, '') as Brands
jens frandsen
la source
15
saint s ** t c'est incroyable! Lorsqu'il est exécuté seul, comme dans votre exemple, le résultat est formaté en tant que lien hypertexte. Lorsque vous cliquez dessus (dans SSMS), une nouvelle fenêtre contenant les données est ouverte, mais lorsqu'il est utilisé dans le cadre d'une requête plus large, il apparaît simplement sous la forme d'une chaîne. Est-ce une chaîne? ou est-ce xml que je dois traiter différemment dans l'application qui utilisera ces données?
Ben
10
Cette approche permet également aux caractères d'échappement XML comme <et>. Ainsi, la sélection de '<b>' + FName + '</b>' entraîne "& lt; b & gt; John & lt; / b & gt; & lt; b & gt; Paul ..."
Lukáš Lánský
8
Solution soignée. Je remarque que même si je n'ajoute pas le + ', 'il ajoute toujours un espace unique entre chaque élément concaténé.
Baodad du
8
@Baodad Cela semble faire partie de l'accord. Vous pouvez contourner ce problème en remplaçant un caractère de jeton ajouté. Par exemple, cela fait une liste parfaite SELECT STUFF(REPLACE((SELECT '#!'+city AS 'data()' FROM #cityzip FOR XML PATH ('')),' #!',', '),1,2,'')
séparée
1
Wow, en fait dans mes tests en utilisant data () et un remplacement est bien plus performant qu'improbable. Super bizarre.
NReilingh
306

Dans SQL Server 2005

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

Dans SQL Server 2016

vous pouvez utiliser la syntaxe FOR JSON

c'est à dire

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

Et le résultat deviendra

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

Cela fonctionnera même si vos données contiennent des caractères XML non valides

le '"},{"_":"'est sûr parce que si vos données contiennent, '"},{"_":"',il sera échappé à"},{\"_\":\"

Vous pouvez remplacer ', 'par n'importe quel séparateur de chaînes


Et dans SQL Server 2017, Azure SQL Database

Vous pouvez utiliser la nouvelle fonction STRING_AGG

Steven Chong
la source
3
Bonne utilisation de la fonction STUFF pour supprimer les deux premiers caractères.
David
3
Je préfère cette solution, car je peux facilement l'utiliser dans une liste de sélection en ajoutant «comme <label>». Je ne sais pas comment faire cela avec la solution de @Ritesh.
R. Schreurs
13
Cela vaut mieux que la réponse acceptée parce que cette option gère également XML caractères un-reserverd échapper tels que <, >, &, etc. , qui FOR XML PATH('')échapperont automatiquement.
BateTech
C'est une réponse impressionnante car elle a résolu le problème et fournit les meilleures façons de faire les choses dans différentes versions de SQL maintenant, je souhaite pouvoir utiliser 2017 / Azure
Chris Ward
120

Dans MySQL, il existe une fonction, GROUP_CONCAT () , qui vous permet de concaténer les valeurs de plusieurs lignes. Exemple:

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
la source
fonctionne bien. Mais quand j'utilise, SEPARATOR '", "'je manquerai quelques caractères à la fin de la dernière entrée. pourquoi cela peut-il arriver?
gooleem
@gooleem Je ne sais pas ce que vous voulez dire, mais cette fonction ne fait que séparer les éléments, pas après. Si ce n'est pas la réponse, je recommanderais de poster une nouvelle question.
Darryl Hein
@ DarrylHein pour mes besoins j'ai utilisé le séparateur comme ci-dessus. Mais cela me coupe quelques caractères à la toute fin de la sortie. C'est très étrange et semble être un bug. Je n'ai pas de solution, je viens de contourner.
gooleem
Fonctionne essentiellement. Deux choses à considérer: 1) si votre colonne n'est pas un CHAR, vous devez le caster, par exemple via GROUP_CONCAT( CAST(id AS CHAR(8)) ORDER BY id ASC SEPARATOR ',')2) si vous avez de nombreuses valeurs à venir, vous devez augmenter le group_concat_max_lencomme écrit dans stackoverflow.com/a/1278210/1498405
hardmooth
58

Utilisez COALESCE - En savoir plus d'ici

À titre d'exemple:

102

103

104

Ensuite, écrivez le code ci-dessous dans le serveur 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 sortie serait:

102,103,104
pedram
la source
2
C'est vraiment la meilleure solution IMO car elle évite les problèmes d'encodage que présente FOR XML. J'ai utilisé Declare @Numbers AS Nvarchar(MAX)et cela a bien fonctionné. Pouvez-vous expliquer pourquoi vous recommandez de ne pas l'utiliser s'il vous plaît?
EvilDr
7
Cette solution a déjà été publiée il y a 8 ans! stackoverflow.com/a/194887/986862
Andre Figueiredo
Pourquoi cette requête renvoie-t-elle ??? symboles au lieu de cyrilliques? Est-ce juste un problème de sortie?
Akmal Salikhov
48

Les tableaux Postgres sont géniaux. Exemple:

Créez des données de test:

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)

Les agréger dans un tableau:

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

Convertissez le tableau en une chaîne séparée par des virgules:

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

TERMINÉ

Depuis PostgreSQL 9.0, c'est encore plus simple .

hgmnz
la source
Si vous avez besoin de plusieurs colonnes, par exemple leur identifiant d'employé entre parenthèses, utilisez l'opérateur de concaténation: select array_to_string(array_agg(name||'('||id||')'
Richard Fox
Ne s'applique pas au serveur sql , uniquement à mysql
GoldBishop
46

Oracle 11g Release 2 prend en charge la fonction LISTAGG. Documentation ici .

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.

Attention

Soyez prudent lors de l'implémentation de cette fonction s'il est possible que la chaîne résultante dépasse 4 000 caractères. Cela lèvera une exception. Si tel est le cas, vous devez gérer l'exception ou lancer votre propre fonction qui empêche la chaîne jointe de dépasser 4 000 caractères.

Alex
la source
1
Pour les anciennes versions d'Oracle, wm_concat est parfait. Son utilisation est expliquée dans le lien cadeau d'Alex. Merci Alex!
toscanelli
LISTAGGfonctionne parfaitement! Lisez simplement le document lié ici. wm_concatsupprimé à partir de la version 12c.
asgs
34

Dans SQL Server 2005 et versions ultérieures, utilisez la requête ci-dessous pour concaténer les lignes.

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
la source
2
Je crois que cela échoue lorsque les valeurs contiennent des symboles XML tels que <ou &.
Sam
28

Je n'ai pas accès à un serveur SQL à la maison, donc je suppose que la syntaxe ici, mais c'est plus ou moins:

DECLARE @names VARCHAR(500)

SELECT @names = @names + ' ' + Name
FROM Names
Dana
la source
11
Vous auriez besoin d'initier @names à quelque chose de non nul, sinon vous obtiendrez NULL partout; vous devrez également gérer le délimiteur (y compris celui qui n'est pas nécessaire)
Marc Gravell
3
le seul problème avec cette approche (que j'utilise tout le temps) est que vous ne pouvez pas l'intégrer
ekkis
1
Pour vous débarrasser de l'espace de tête, modifiez la requête enSELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ' ' END + Name FROM Names
Tian van Heerden
De plus, vous devez vérifier que le nom n'est pas nul, vous pouvez le faire en faisant:SELECT @names = @names + ISNULL(' ' + Name, '')
Vita1ij
28

Une solution CTE récursive a été suggérée, mais aucun code n'a été fourni. Le code ci-dessous est un exemple de CTE récursif. Notez que bien que les résultats correspondent à la question, les données ne correspondent pas tout à fait à la description donnée, car je suppose que vous voulez vraiment faire cela sur des groupes de lignes, pas toutes les lignes du tableau. Le modifier pour qu'il corresponde à toutes les lignes du tableau reste un exercice pour le lecteur.

;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
la source
1
Pour les sidérés: cette requête insère 12 lignes (a 3 colonnes) dans une table de base temporaire, puis crée une expression de table commune récursive (rCTE), puis aplatit la name colonne en une chaîne séparée par des virgules pour 4 groupes de ids. À première vue, je pense que c'est plus de travail que ce que font la plupart des autres solutions pour SQL Server.
knb
2
@knb: je ne sais pas si c'est une louange, une condamnation ou simplement une surprise. La table de base est parce que j'aime que mes exemples fonctionnent réellement, cela n'a rien à voir avec la question.
jmoreno
26

À partir de PostgreSQL 9.0, c'est assez simple:

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

Dans les versions antérieures à 9.0 array_agg()peuvent être utilisées comme indiqué par hgmnz

un cheval sans nom
la source
Pour ce faire avec des colonnes qui ne sont pas de type texte, vous devez ajouter un SELECT string_agg(non_text_type::text, ',') FROM table
transtypage de
@TorbenKohlmeier: vous n'en avez besoin que pour les colonnes sans caractère (par exemple entier, décimal). Cela fonctionne très bien pour varcharouchar
a_horse_with_no_name
26

Vous devez créer une variable qui contiendra votre résultat final et le sélectionner, comme ça.

Solution la plus simple

DECLARE @char VARCHAR(MAX);

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

PRINT @char;
Tigerjz32
la source
18

L'utilisation de XML m'a aidé à séparer les lignes par des virgules. Pour la virgule supplémentaire, nous pouvons utiliser la fonction de remplacement de SQL Server. Au lieu d'ajouter une virgule, l'utilisation de l'AS 'data ()' concaténera les lignes avec des espaces, qui pourront ensuite être remplacés par des virgules comme syntaxe écrite ci-dessous.

REPLACE(
        (select FName AS 'data()'  from NameList  for xml path(''))
         , ' ', ', ') 
Diwakar
la source
2
C'est la meilleure réponse ici à mon avis. L'utilisation de la variable declare n'est pas bonne lorsque vous avez besoin de vous joindre à une autre table, ce qui est agréable et court. Bon travail.
David Roussel
7
cela ne fonctionne pas bien si les données FName ont déjà des espaces, par exemple "Mon nom"
binball
Vraiment, cela fonctionne pour moi sur ms-sql 2016 Sélectionnez REMPLACER ((sélectionnez Nom AS 'data ()' de Brand Where Id IN (1,2,3,4) pour le chemin xml ('')), '', ' , ') comme allBrands
Rejwanul Reja
17

Une solution prête à l'emploi, sans virgule supplémentaire:

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

Une liste vide entraînera une valeur NULL. Habituellement, vous insérerez la liste dans une colonne de tableau ou une variable de programme: ajustez la longueur maximale de 255 selon vos besoins.

(Diwakar et Jens Frandsen ont fourni de bonnes réponses, mais doivent être améliorées.)

Daniel Reis
la source
Il y a un espace avant la virgule lors de l'utilisation de ceci :(
slayernoah
1
Remplacez simplement ', 'par ','si vous ne voulez pas d'espace supplémentaire.
Daniel Reis
13
SELECT STUFF((SELECT ', ' + name FROM [table] FOR XML PATH('')), 1, 2, '')

Voici un exemple:

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
la source
10
DECLARE @Names VARCHAR(8000)
SELECT @name = ''
SELECT @Names = @Names + ',' + Names FROM People
SELECT SUBSTRING(2, @Names, 7998)

Cela place la virgule errante au début.

Cependant, si vous avez besoin d'autres colonnes, ou pour CSV une table enfant, vous devez l'encapsuler dans un champ défini par l'utilisateur scalaire (UDF).

Vous pouvez également utiliser le chemin XML comme sous-requête corrélée dans la clause SELECT (mais je devrais attendre de retourner au travail parce que Google ne fait pas de travail à la maison :-)

gbn
la source
10

Avec les autres réponses, la personne qui lit la réponse doit connaître une table de domaine spécifique telle que véhicule ou étudiant. La table doit être créée et remplie de données pour tester une solution.

Voici un exemple qui utilise la table SQL Server "Information_Schema.Columns". En utilisant cette solution, aucune table ne doit être créée ni aucune donnée ajoutée. Cet exemple crée une liste de noms de colonnes séparés par des virgules pour toutes les tables de la base de données.

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

Pour les bases de données Oracle, consultez cette question: comment concaténer plusieurs lignes en une seule dans Oracle sans créer de procédure stockée?

La meilleure réponse semble être de @Emmanuel, en utilisant la fonction intégrée LISTAGG (), disponible dans Oracle 11g version 2 et versions ultérieures.

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

comme l'a souligné @ user762952, et selon la documentation d'Oracle http://www.oracle-base.com/articles/misc/string-aggregation-techniques.php , la fonction WM_CONCAT () est également une option. Il semble stable, mais Oracle recommande explicitement de ne pas l'utiliser pour toute application SQL, donc utilisez-le à vos risques et périls.

En dehors de cela, vous devrez écrire votre propre fonction; le document Oracle ci-dessus contient un guide sur la façon de procéder.

ZeroK
la source
7

Pour éviter les valeurs nulles, vous pouvez utiliser CONCAT ()

DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name) 
FROM Names
select @names
Rapunzo
la source
Ce serait bien de savoir pourquoi CONCAT fonctionne. Un lien vers MSDN serait bien.
Ingénieur inversé
7

J'ai vraiment aimé l'élégance de la réponse de Dana . Je voulais juste le terminer.

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
la source
Si vous supprimez les deux derniers symboles «,», vous devez ajouter «,» après Nom («SELECT \ @names = \ @names + Name +», «FROM Names»). De cette façon, les deux derniers caractères seront toujours ",".
JT_
Dans mon cas, je devais me débarrasser de la virgule principale, alors changez la requête en SELECT @names = @names + CASE WHEN LEN(@names)=0 THEN '' ELSE ', ' END + Name FROM Namesalors vous n'aurez pas à la tronquer par la suite.
Tian van Heerden
6

Cette réponse nécessitera certains privilèges sur le serveur pour fonctionner.

Les assemblages sont une bonne option pour vous. Il existe de nombreux sites qui expliquent comment le créer. Celui que je pense est très bien expliqué est celui- ci

Si vous le souhaitez, j'ai déjà créé l'assemblage, et il est possible de télécharger la DLL ici .

Une fois que vous l'avez téléchargé, vous devrez exécuter le script suivant dans votre serveur SQL:

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

Notez que le chemin d'accès à l'assembly peut être accessible au serveur. Puisque vous avez réussi toutes les étapes, vous pouvez utiliser la fonction comme:

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

J'espère que cela aide!!!

Nizam
la source
1
Le lien DLL est une erreur 404. L'utilisation d'un assemblage pour cela est exagéré. Voir la meilleure réponse pour SQL Server.
Protiguous
6

Exemple complet MySQL:

Nous avons des utilisateurs qui peuvent avoir de nombreuses données et nous voulons avoir une sortie, où nous pouvons voir toutes les données des utilisateurs dans une liste:

Résultat:

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

Configuration de la table:

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

Requete:

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
user1767754
la source
5

J'utilise généralement select comme ceci pour concaténer des chaînes dans 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
la source
5

Si vous souhaitez traiter les valeurs nulles, vous pouvez le faire en ajoutant une clause where ou en ajoutant une autre COALESCE autour de la première.

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

Cela a fonctionné pour moi ( SqlServer 2016 ):

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

Voici la source: https://www.mytecbits.com/

Et une solution pour MySql (puisque cette page apparaît dans Google pour MySql)

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

De MySql Documentations

Arash.Zandi
la source
4

Dans Oracle, c'est le cas wm_concat. Je crois que cette fonction est disponible dans la version 10g et supérieure.

user762952
la source
4

Cela peut aussi être utile

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

Retour

Peter,Paul,Mary
endo64
la source
5
Malheureusement, ce comportement ne semble pas officiellement pris en charge. MSDN dit: "Si une variable est référencée dans une liste de sélection, une valeur scalaire doit lui être affectée ou l'instruction SELECT ne doit renvoyer qu'une seule ligne." Et il y a des gens qui ont observé des problèmes: sqlmag.com/sql-server/multi-row-variable-assignment-and-order
blueling