Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Que fait cette requête pour créer une liste délimitée par des virgules SQL Server ?

La façon la plus simple de l'expliquer est de regarder comment FOR XML PATH fonctionne pour XML réel. Imaginez un simple tableau Employee :

EmployeeID      Name
1               John Smith
2               Jane Doe

Vous pouvez utiliser

SELECT  EmployeeID, Name
FROM    emp.Employee
FOR XML PATH ('Employee')

Cela créerait XML comme suit

<Employee>
    <EmployeeID>1</EmployeeID>
    <Name>John Smith</Name>
</Employee>
<Employee>
    <EmployeeID>2</EmployeeID>
    <Name>Jane Doe</Name>
</Employee>

Suppression de 'Employee' de PATH supprime les balises xml externes donc cette requête :

SELECT  Name
FROM    Employee
FOR XML PATH ('')

créerait

    <Name>John Smith</Name>
    <Name>Jane Doe</Name>

Ce que vous faites alors n'est pas idéal, le nom de la colonne 'data()' force une erreur sql car il essaie de créer une balise xml qui n'est pas une balise légale, donc l'erreur suivante est générée :

Le nom de colonne 'Data()' contient un identifiant XML non valide, comme requis par FOR XML ; '('(0x0028) est le premier caractère fautif.

La sous-requête corrélée masque cette erreur et génère simplement le XML sans balise :

SELECT  Name AS [Data()]
FROM    Employee
FOR XML PATH ('')

crée

John Smith Jane Doe

Vous remplacez alors les espaces par des virgules, assez explicites...

Si j'étais vous, j'adapterais légèrement la requête :

SELECT  E1.deptno, 
        STUFF(( SELECT  ', ' + E2.ename 
                FROM    emp AS e2 
                WHERE   e1.deptno = e2.DEPTNO 
                FOR XML PATH('')
            ), 1, 2, '') 
FROM    EMP AS e1 
GROUP BY DEPTNO; 

L'absence d'alias de colonne signifie qu'aucune balise xml n'est créée, et l'ajout de la virgule dans la requête de sélection signifie que tous les noms contenant des espaces ne provoqueront pas d'erreurs,STUFF supprimera la première virgule et l'espace.

ADDENDA

Pour développer ce que KM a dit dans un commentaire, car cela semble obtenir quelques vues supplémentaires, la bonne façon d'échapper aux caractères XML serait d'utiliser .value comme suit :

SELECT  E1.deptno, 
        STUFF(( SELECT  ', ' + E2.ename 
                FROM    emp AS e2 
                WHERE   e1.deptno = e2.DEPTNO 
                FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)'), 1, 2, '') 
FROM    EMP AS e1 
GROUP BY DEPTNO;