Obtenir des listes STRING_AGG fiables dans SQL Server
Définissez ordre, doublons, valeurs absentes et longueur des listes, puis choisissez JSON lorsque les données doivent conserver une structure exacte.
Une liste séparée par des virgules ressemble à un problème de présentation, mais sa justesse dépend des données. Quelles lignes appartiennent au groupe? Les doublons comptent-ils? Une valeur absente doit-elle laisser une place? Quel ordre est attendu? STRING_AGG raccourcit la concaténation sans définir ces règles.
Les exemples utilisent SQL Server 2017 ou ultérieur. La clause ordonnée WITHIN GROUP demande un niveau de compatibilité d'au moins 110. Vérifiez moteur et compatibilité lorsqu'une même instruction fonctionne différemment entre environnements.
Préciser ordre et valeurs absentes
Les données contiennent volontairement une étiquette répétée et NULL.
DECLARE @Tags table (DocumentId int, TagId int, Tag nvarchar(100));
INSERT @Tags VALUES
(1, 1, N'Tuning'), (1, 2, N'Backup'), (1, 3, N'Tuning'),
(1, 4, NULL), (2, 5, N'Security');
SELECT DocumentId,
STRING_AGG(CONVERT(nvarchar(max), Tag), N', ')
WITHIN GROUP (ORDER BY Tag, TagId) AS TagList
FROM @Tags
GROUP BY DocumentId;
Pour le document 1, l'ordre alphabétique illustré donne Backup, Tuning, Tuning. NULL ne contribue ni texte ni séparateur supplémentaire. Le document 2 donne Security. STRING_AGG ne déduplique pas. Un ORDER BY extérieur trierait les groupes retournés, pas les éléments de chaque liste.
WITHIN GROUP fixe l'ordre interne. TagId départage les étiquettes comparées égales. Si le métier demande l'ordre d'attribution, utilisez sa séquence réelle. La collation influence comparaison et classement des accents ou de la casse; testez les textes réellement présents.
NULL diffère d'une chaîne vide. Le premier est ignoré, la seconde reste une valeur et peut produire un séparateur surprenant. Normalisez les entrées vides ou uniquement composées d'espaces à une frontière convenue. Ne modifiez pas des codes significatifs uniquement pour embellir un rapport.
Un libellé pour les absences doit être substitué avant l'agrégation. Choisissez-le sans ambiguïté avec une vraie valeur, ou utilisez une structure contenant un véritable null. Afficher Inconnu ne change pas le fait que l'information source manque.
Dédupliquer au bon niveau
Pour une liste d'étiquettes uniques, retirez les doublons dans la bonne relation avant de concaténer.
;WITH DistinctTags AS (
SELECT DISTINCT DocumentId, Tag
FROM @Tags
WHERE Tag IS NOT NULL
)
SELECT DocumentId,
STRING_AGG(CONVERT(nvarchar(max), Tag), N', ')
WITHIN GROUP (ORDER BY Tag) AS TagList
FROM DistinctTags
GROUP BY DocumentId;
Le document 1 produit maintenant Backup, Tuning. DISTINCT porte ensemble sur DocumentId et Tag. La même étiquette reste donc possible dans un autre document. Dédupliquer globalement puis reconstruire les associations ferait perdre l'information de rattachement.
Examinez les jointures avant d'ajouter DISTINCT. Plusieurs commentaires et plusieurs étiquettes par document peuvent multiplier les lignes intermédiaires. Une déduplication finale peut cacher cette multiplication tandis que d'autres sommes restent fausses. Agrégez les étiquettes séparément puis joignez le résultat à une ligne par document.
La comparaison textuelle compte aussi. Sous une collation insensible à la casse, des graphies différentes peuvent fusionner. Si une graphie canonique est nécessaire, définissez sa sélection explicitement. DISTINCT ne constitue pas à lui seul une politique de libellé préféré.
Maîtriser taille et format de sortie
Convertissez l'expression d'entrée en nvarchar(max) avant STRING_AGG lorsque les longues listes sont légitimes. Le type du résultat dérive de l'entrée. Convertir après l'agrégation arrive trop tard pour éviter sa limite initiale. Gardez également compatibles les types de valeur et séparateur.
Un type volumineux ne justifie pas une réponse sans limite. Des centaines de milliers d'éléments peuvent demander beaucoup de mémoire et produire une charge réseau énorme. Définissez des limites, paginez les relations ou créez un export distinct pour les groupes exceptionnels.
Une liste de virgules n'est pas un format d'échange sans perte si les valeurs contiennent des virgules, guillemets ou retours à la ligne. FOR JSON préserve structure et échappement pour une API. La chaîne ne doit pas non plus remplacer la relation normalisée si chaque étiquette doit rester filtrable, validable ou modifiable.
Testez doublons, NULL, chaînes vides, textes multilingues, séparateurs dans les valeurs et résultats longs. En cas de troncature apparente, comparez la longueur réelle en base aux limites d'affichage du client. Une agrégation fiable conserve le sens jusqu'au destinataire.
Références techniques: Microsoft Learn: STRING_AGG · Microsoft Learn: FOR JSON.