La table est:
+----+------+
| Id | Name |
+----+------+
| 1 | aaa |
| 1 | bbb |
| 1 | ccc |
| 1 | ddd |
| 1 | eee |
+----+------+
Sortie requise:
+----+---------------------+
| Id | abc |
+----+---------------------+
| 1 | aaa,bbb,ccc,ddd,eee |
+----+---------------------+
Requete:
SELECT ID,
abc = STUFF(
(SELECT ',' + name FROM temp1 FOR XML PATH ('')), 1, 1, ''
)
FROM temp1 GROUP BY id
Cette requête fonctionne correctement. Mais j’ai juste besoin d’explications sur son fonctionnement ou existe-t-il un autre moyen ou un moyen court de le faire?.
Je suis très confus de comprendre cela.
Voici comment cela fonctionne:
1. Obtenir la chaîne d'élément XML avec FOR XML
L'ajout de FOR XML PATH à la fin d'une requête permet de générer les résultats de la requête sous forme d'éléments XML, avec le nom de l'élément contenu dans l'argument PATH. Par exemple, si nous devions exécuter l'instruction suivante:
SELECT ',' + name
FROM temp1
FOR XML PATH ('')
En passant une chaîne vide (FOR XML PATH ('')), nous obtenons ce qui suit:
,aaa,bbb,ccc,ddd,eee
2. Enlever la virgule avec STUFF
L'instruction STUFF littéralement "encapsule" une chaîne dans une autre, en remplaçant les caractères de la première chaîne. Nous l'utilisons toutefois simplement pour supprimer le premier caractère de la liste de valeurs résultante.
SELECT abc = STUFF((
SELECT ',' + NAME
FROM temp1
FOR XML PATH('')
), 1, 1, '')
FROM temp1
Les paramètres de STUFF
sont:
Donc on se retrouve avec:
aaa,bbb,ccc,ddd,eee
. Rejoignez sur id pour obtenir la liste complète
Ensuite, nous rejoignons simplement ceci dans la liste des identifiants de la table temporaire, pour obtenir une liste des identifiants avec nom:
SELECT ID, abc = STUFF(
(SELECT ',' + name
FROM temp1 t1
WHERE t1.id = t2.id
FOR XML PATH (''))
, 1, 1, '') from temp1 t2
group by id;
Et nous avons notre résultat:
-----------------------------------
| Id | Name |
|---------------------------------|
| 1 | aaa,bbb,ccc,ddd,eee |
-----------------------------------
J'espère que cela t'aides!
Cet article couvre diverses manières de concaténer des chaînes en SQL, y compris une version améliorée de votre code qui ne code pas en XML les valeurs concaténées.
SELECT ID, abc = STUFF
(
(
SELECT ',' + name
FROM temp1 As T2
-- You only want to combine rows for a single ID here:
WHERE T2.ID = T1.ID
ORDER BY name
FOR XML PATH (''), TYPE
).value('.', 'varchar(max)')
, 1, 1, '')
FROM temp1 As T1
GROUP BY id
Pour comprendre ce qui se passe, commencez par la requête interne:
SELECT ',' + name
FROM temp1 As T2
WHERE T2.ID = 42 -- Pick a random ID from the table
ORDER BY name
FOR XML PATH (''), TYPE
Comme vous spécifiez FOR XML
, vous obtiendrez une seule ligne contenant un fragment XML représentant toutes les lignes.
Comme vous n'avez pas spécifié d'alias de colonne pour la première colonne, chaque ligne est encapsulée dans un élément XML dont le nom est indiqué entre crochets après le FOR XML PATH
. Par exemple, si vous avez FOR XML PATH ('X')
, vous obtiendrez un document XML ressemblant à ceci:
<X>,aaa</X>
<X>,bbb</X>
...
Mais, puisque vous n'avez pas spécifié de nom d'élément, vous obtenez simplement une liste de valeurs:
,aaa,bbb,...
La .value('.', 'varchar(max)')
récupère simplement la valeur du fragment XML résultant, sans codage XML des caractères "spéciaux". Vous avez maintenant une chaîne qui ressemble à:
',aaa,bbb,...'
La fonction STUFF
supprime ensuite la virgule principale et vous donne un résultat final ressemblant à:
'aaa,bbb,...'
Cela semble assez déroutant au premier abord, mais il a tendance à donner de très bons résultats par rapport à certaines des autres options.
le mode PATH est utilisé pour générer du XML à partir d'une requête SELECT
1. SELECT
ID,
Name
FROM temp1
FOR XML PATH;
Ouput:
<row>
<ID>1</ID>
<Name>aaa</Name>
</row>
<row>
<ID>1</ID>
<Name>bbb</Name>
</row>
<row>
<ID>1</ID>
<Name>ccc</Name>
</row>
<row>
<ID>1</ID>
<Name>ddd</Name>
</row>
<row>
<ID>1</ID>
<Name>eee</Name>
</row>
La sortie est un XML centré sur les éléments, dans lequel chaque valeur de colonne de l'ensemble de lignes résultant est encapsulée dans un élément de ligne. Étant donné que la clause SELECT ne spécifie aucun alias pour les noms de colonne, les noms d'élément enfant générés sont les mêmes que les noms de colonne correspondants dans la clause SELECT.
Une balise est ajoutée pour chaque ligne de l'ensemble de lignes.
2.
SELECT
ID,
Name
FROM temp1
FOR XML PATH('');
Ouput:
<ID>1</ID>
<Name>aaa</Name>
<ID>1</ID>
<Name>bbb</Name>
<ID>1</ID>
<Name>ccc</Name>
<ID>1</ID>
<Name>ddd</Name>
<ID>1</ID>
<Name>eee</Name>
Pour l'étape 2: Si vous spécifiez une chaîne de longueur nulle, l'élément d'habillage n'est pas produit.
3.
SELECT
Name
FROM temp1
FOR XML PATH('');
Ouput:
<Name>aaa</Name>
<Name>bbb</Name>
<Name>ccc</Name>
<Name>ddd</Name>
<Name>eee</Name>
4. SELECT
',' +Name
FROM temp1
FOR XML PATH('')
Ouput:
,aaa,bbb,ccc,ddd,eee
À l'étape 4, nous concaténons les valeurs.
5. SELECT ID,
abc = (SELECT
',' +Name
FROM temp1
FOR XML PATH('') )
FROM temp1
Ouput:
1 ,aaa,bbb,ccc,ddd,eee
1 ,aaa,bbb,ccc,ddd,eee
1 ,aaa,bbb,ccc,ddd,eee
1 ,aaa,bbb,ccc,ddd,eee
1 ,aaa,bbb,ccc,ddd,eee
6. SELECT ID,
abc = (SELECT
',' +Name
FROM temp1
FOR XML PATH('') )
FROM temp1 GROUP by iD
Ouput:
ID abc
1 ,aaa,bbb,ccc,ddd,eee
À l'étape 6, nous regroupons la date par ID.
STUFF (source_string, start, length, add_string) Paramètres ou arguments chaîne_source chaîne source à modifier. Start Position dans la chaîne source pour supprimer les caractères de longueur, puis insérer add_string. Length Nombre de caractères à supprimer de la chaîne source. Add_string La séquence de caractères à insérer dans la chaîne source à la position de départ.
SELECT ID,
abc =
STUFF (
(SELECT
',' +Name
FROM temp1
FOR XML PATH('')), 1, 1, ''
)
FROM temp1 GROUP by iD
Output:
-----------------------------------
| Id | Name |
|---------------------------------|
| 1 | aaa,bbb,ccc,ddd,eee |
-----------------------------------
Azure SQL Database et SQL Server (à compter de 2017) comportent de toutes nouvelles fonctionnalités pour gérer ce scénario précis. Je pense que cela servirait de méthode officielle native pour ce que vous essayez d'accomplir avec la méthode XML/STUFF. Exemple:
select id, STRING_AGG(name, ',') as abc
from temp1
group by id
STRING_AGG - https://msdn.Microsoft.com/en-us/library/mt790580.aspx
EDIT: Lors de la publication de cette information, j'ai mentionné SQL Server 2016 car je pensais l'avoir vue sur une fonctionnalité potentielle à inclure. Soit je me suis souvenu de cela de manière incorrecte ou quelque chose a changé, merci pour l'édition suggérée pour réparer la version. En outre, très impressionné et n'était pas pleinement conscient du processus de révision en plusieurs étapes qui m'a amené à une option finale.
Dans for xml path
, si nous définissons une valeur telle que [ for xml path('ENVLOPE') ]
, ces balises seront ajoutées à chaque ligne:
<ENVLOPE>
</ENVLOPE>
SELECT ID,
abc = STUFF(
(SELECT ',' + name FROM temp1 FOR XML PATH ('')), 1, 1, ''
)
FROM temp1 GROUP BY id
Ici, dans la requête ci-dessus STUFF la fonction est utilisée pour simplement supprimer la première virgule (,)
de la chaîne xml générée (,aaa,bbb,ccc,ddd,eee)
puis elle deviendra (aaa,bbb,ccc,ddd,eee)
.
Et FOR XML PATH('')
convertit simplement les données de colonne en (,aaa,bbb,ccc,ddd,eee)
chaîne, mais dans PATH, nous passons '' afin d'éviter la création d'une balise XML.
Et à la fin, nous avons regroupé les enregistrements en utilisant la colonne ID.
Declare @Temp As Table (Id Int,Name Varchar(100))
Insert Into @Temp values(1,'A'),(1,'B'),(1,'C'),(2,'D'),(2,'E'),(3,'F'),(3,'G'),(3,'H'),(4,'I'),(5,'J'),(5,'K')
Select X.ID,
stuff((Select ','+ Z.Name from @Temp Z Where X.Id =Z.Id For XML Path('')),1,1,'')
from @Temp X
Group by X.ID
J'ai fait le débogage et finalement renvoyé ma requête 'bourrée' à elle de manière normale.
Simplement
select * from myTable for xml path('myTable')
me donne le contenu de la table pour écrire dans une table de journal à partir d'un déclencheur que je débogue.