这个查询创建逗号分隔列表SQL Server的作用是什么?

时间:2012-04-30 09:42:17

标签: sql sql-server sql-server-2008

我在google的帮助下编写了这个查询,从表中创建了一个分隔列表,但我对此查询没有任何理解。

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

 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

4 个答案:

答案 0 :(得分:36)

解释它的最简单方法是查看FOR XML PATH如何为实际XML工作。想象一个简单的表Employee

EmployeeID      Name
1               John Smith
2               Jane Doe

您可以使用

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

这将创建XML如下

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

PATH删除'Employee'会删除外部xml标记,以便进行此查询:

SELECT  Name
FROM    Employee
FOR XML PATH ('')

会创建

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

您正在做的事情并不理想,列名'data()'会强制导致sql错误,因为它正在尝试创建一个不合法标记的xml标记,因此会生成以下错误:

  

列名'Data()'包含FOR XML所需的无效XML标识符; '('(0x0028)是第一个出错的角色。

相关子查询隐藏了此错误,只生成没有标记的XML:

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

创建

John Smith Jane Doe

然后用逗号替换空格,相当自我解释......

如果我是你,我会略微调整查询:

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; 

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

<强>附录

要详细说明知识管理在评论中所说的内容,因为这似乎可以获得更多视图,转义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 :(得分:6)

从内到外逐步分开。

第1步:

运行最里面的查询并查看它产生的内容:

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

你应该得到类似的输出:

CLARK KING MILLER

第2步:

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

CLARK, KING, MILLER

第3步:

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

答案 2 :(得分:1)

SQL Server 2017 使new STRING_AGG更容易。最近发现这篇文章并切换我的 STUFF / FOR XML 策略以使用新的字符串函数。还避免了需要额外的JOIN / SUBQUERY以及FOR XML(和奇数编码问题)的开销,并且难以解释SQL。

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

注意:还要确保check out the counterpart STRING_SPLIT更容易使用SQL分隔数据。

答案 3 :(得分:0)

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