【问题标题】:What this query does to create comma delimited list SQL Server?此查询对创建逗号分隔列表 SQL Server 做了什么?
【发布时间】:2012-05-10 00:35:19
【问题描述】:

我在 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

【问题讨论】:

  • 请注意,您的代码将无法处理包含类似字符的文本 > 您将获得像 <>& 这样的字符扩展,还有更好的进行这种连接的方法,请参阅:stackoverflow.com/a/5031297/65223

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


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    一步一步地拆开它——从里到外。

    第 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 值 - 加上内部查询的结果 - 并产生最终结果。

    【讨论】:

      【解决方案3】:

      解释它的最简单方法是查看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 中删除“员工”会删除外部 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 将删除第一个逗号和空格。

      附录

      为了详细说明 KM 在评论中所说的话,因为这似乎获得了更多的意见,转义 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 很好的解释,但 请注意此代码将无法处理包含字符的文本,例如 > 你会得到像 &amp;lt;&amp;gt; @987654338 这样的字符扩展@有更好的方法来做这种连接,见:stackoverflow.com/a/5031297/65223
      • 你缺少一个单引号来结束'NVARCHAR(MAX)'
      【解决方案4】:

      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 分隔数据。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-09-12
        • 2015-11-29
        • 1970-01-01
        • 1970-01-01
        • 2011-12-27
        相关资源
        最近更新 更多