这个查询做什么来创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定界数据更容易。