这个查询做什么来创build逗号分隔列表SQL Server?

我已经写了这个查询与谷歌的帮助来创build一个表中的分隔列表,但我不明白从这个查询任何东西。

任何人都可以解释我发生了什么事

SELECT E1.deptno, allemp = Replace ((SELECT E2.ename AS 'data()' FROM emp AS e2 WHERE e1.deptno = e2.DEPTNO FOR xml PATH('')), ' ', ', ') FROM EMP AS e1 GROUP BY DEPTNO; 

给我结果

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

解释它的最简单的方法是查看FOR XML PATH如何用于实际的XML。 设想一个简单的表Employee

 EmployeeID Name 1 John Smith 2 Jane Doe 

你可以使用

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

这将创buildXML如下

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

PATH删除“员工”删除外部XML标签,所以这个查询:

 SELECT Name FROM Employee FOR XML PATH ('') 

会创build

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

那么你在做什么并不理想,列名'data()'会强制执行一个sql错误,因为它试图创build一个不是合法标签的xml标签,所以会产生下面的错误:

列名'Data()'包含FOR XML所要求的无效XML标识符; '('(0x0028)是错误的第一个字符。

相关的子查询隐藏了这个错误,只是生成没有标签的XML:

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

创build

 John Smith Jane Doe 

然后你用逗号replace空格,相当自我解释…

如果我是你,我会稍微修改查询:

 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; 

没有列别名意味着不会创buildXML标签,并且在select查询中添加逗号意味着任何带有空格的名称都不会导致错误, STUFF将删除第一个逗号和空格。

附录

为了详细说明知识pipe理在评论中所说的内容,因为这似乎越来越多的观点,转义XML字符的正确方法是使用.value如下:

 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; 

从内到外一步一步把它分开。

步骤1:

运行最内层的查询,看看它产生了什么:

 SELECT E2.ename AS 'data()' FROM emp AS e2 WHERE e2.DEPTNO = 10 FOR XML PATH('') 

你应该得到如下的输出:

 CLARK KING MILLER 

第2步:

REPLACE只是用空格replace空格,从而将输出转换为

 CLARK, KING, MILLER 

第3步:

外部查询获取deptno值 – 加上内部查询的结果 – 并产生最终结果。

外部查询检索部门号码列表,然后为每个部门号码运行子查询以返回属于该部门的所有名称。 子查询使用FOR XML语句将输出格式化为单行逗号分隔列表。

SQL Server 2017使新STRING_AGG变得更容易。 最近遇到这个post,并切换我的STUFF / FOR XML策略使用新的string函数。 还避免了需要额外的JOIN / SUBQUERY和FOR XML的开销( 以及奇数编码问题 ),而且很难解释SQL。

 SELECT E1.deptno, STRING_AGG(E1.ename, ', ') AS allemp FROM EMP AS e1 GROUP BY DEPTNO; 

注意 :另外一定要查看对应的STRING_SPLIT ,使得使用SQL定界数据更容易。