在SQL Server中如何工作和'为Xml路径'工作

表格是:

+----+------+ | Id | Name | +----+------+ | 1 | aaa | | 1 | bbb | | 1 | ccc | | 1 | ddd | | 1 | eee | +----+------+ 

要求的输出:

 +----+---------------------+ | Id | abc | +----+---------------------+ | 1 | aaa,bbb,ccc,ddd,eee | +----+---------------------+ 

查询:

 SELECT ID, abc = STUFF( (SELECT ',' + name FROM temp1 FOR XML PATH ('')), 1, 1, '' ) FROM temp1 GROUP BY id 

此查询正常工作。 但是我只需要解释它是如何工作的,或者是否有任何其他或简短的方法来做到这一点。

我很困惑,理解这一点。

下面是它的工作原理:

1.使用FOR XML获取XML元素字符串

将FOR XML PATH添加到查询的末尾允许您将查询的结果作为XML元素输出,其元素名称包含在PATH参数中。 例如,如果我们运行以下语句:

 SELECT ',' + name FROM temp1 FOR XML PATH ('') 

通过传入一个空白字符串(FOR XML PATH('')),我们得到以下内容:

 ,aaa,bbb,ccc,ddd,eee 

2.用STUFF删除前面的逗号

STUFF语句将一个字符串“填充”到另一个字符串中,替换第一个字符串中的字符。然而,我们只是用它来删除结果列表的第一个字符。

 SELECT abc = STUFF(( SELECT ',' + NAME FROM temp1 FOR XML PATH('') ), 1, 1, '') FROM temp1 

STUFF的参数是:

  • 字符串被“填充”(在我们的例子中,带有逗号的完整列表)
  • 开始删除和插入字符的位置(1,我们填入空白字符串)
  • 要删除的字符数(1,作为前导逗号)

所以我们结束了:

 aaa,bbb,ccc,ddd,eee 

3.加入ID获取完整列表

接下来,我们只需将它加入临时表中的id列表中,即可得到名称为ID的列表:

 SELECT ID, abc = STUFF( (SELECT ',' + name FROM temp1 t1 WHERE t1.id = t2.id FOR XML PATH ('')) , 1, 1, '') from temp1 t2 group by id; 

我们有我们的结果:

 ----------------------------------- | Id | Name | |---------------------------------| | 1 | aaa,bbb,ccc,ddd,eee | ----------------------------------- 

希望这可以帮助!

本文介绍了在SQL中连接字符串的各种方法,包括代码的改进版本,它不对连接值进行XML编码。

 SELECT ID, abc = STUFF ( ( SELECT ',' + name FROM temp1 As T2 -- You only want to combine rows for a single ID here: WHERE T2.ID = T1.ID ORDER BY name FOR XML PATH (''), TYPE ).value('.', 'varchar(max)') , 1, 1, '') FROM temp1 As T1 GROUP BY id 

要理解发生了什么,从内部查询开始:

 SELECT ',' + name FROM temp1 As T2 WHERE T2.ID = 42 -- Pick a random ID from the table ORDER BY name FOR XML PATH (''), TYPE 

因为你指定了FOR XML ,所以你将得到一个包含代表所有行的XML片段的单行。

由于您没有为第一列指定列别名,因此每行将被包装在XML元素中,并在FOR XML PATH之后的括号中指定名称。 例如,如果你有FOR XML PATH ('X') ,你会得到一个如下所示的XML文档:

 <X>,aaa</X> <X>,bbb</X> ... 

但是,既然你没有指定一个元素的名字,你只是得到一个值列表:

 ,aaa,bbb,... 

.value('.', 'varchar(max)')只需从生成的XML片段中检索值,而不用任何“特殊”字符的XML编码。 你现在有一个字符串,看起来像:

 ',aaa,bbb,...' 

STUFF函数然后删除STUFF的逗号,给你最后的结果,看起来像:

 'aaa,bbb,...' 

乍看起来,这看起来相当混乱,但与其他一些选择相比,它表现得相当好。

PATH模式用于从SELECT查询中生成XML

 1. SELECT ID, Name FROM temp1 FOR XML PATH; Ouput: <row> <ID>1</ID> <Name>aaa</Name> </row> <row> <ID>1</ID> <Name>bbb</Name> </row> <row> <ID>1</ID> <Name>ccc</Name> </row> <row> <ID>1</ID> <Name>ddd</Name> </row> <row> <ID>1</ID> <Name>eee</Name> </row> 

Output是以元素为中心的XML,其中生成的行集中的每个列值都包装在一个行元素中。 因为SELECT子句没有为列名指定任何别名,所以生成的子元素名称与SELECT子句中相应的列名称相同。

对于行集中的每一行,都会添加一个标签。

 2. SELECT ID, Name FROM temp1 FOR XML PATH(''); Ouput: <ID>1</ID> <Name>aaa</Name> <ID>1</ID> <Name>bbb</Name> <ID>1</ID> <Name>ccc</Name> <ID>1</ID> <Name>ddd</Name> <ID>1</ID> <Name>eee</Name> 

对于第2步:如果您指定一个零长度的字符串,则不会生成包装元素。

 3. SELECT Name FROM temp1 FOR XML PATH(''); Ouput: <Name>aaa</Name> <Name>bbb</Name> <Name>ccc</Name> <Name>ddd</Name> <Name>eee</Name> 4. SELECT ',' +Name FROM temp1 FOR XML PATH('') Ouput: ,aaa,bbb,ccc,ddd,eee 

在步骤4中,我们将这些值连接起来。

 5. SELECT ID, abc = (SELECT ',' +Name FROM temp1 FOR XML PATH('') ) FROM temp1 Ouput: 1 ,aaa,bbb,ccc,ddd,eee 1 ,aaa,bbb,ccc,ddd,eee 1 ,aaa,bbb,ccc,ddd,eee 1 ,aaa,bbb,ccc,ddd,eee 1 ,aaa,bbb,ccc,ddd,eee 6. SELECT ID, abc = (SELECT ',' +Name FROM temp1 FOR XML PATH('') ) FROM temp1 GROUP by iD Ouput: ID abc 1 ,aaa,bbb,ccc,ddd,eee 

在第6步中,我们按ID分组日期。

STUFF(source_string,start,length,add_string)参数或参数source_string要修改的源字符串。 启动source_string中的位置以删除长度字符,然后插入add_string。 length从source_string中删除的字符数。 add_string在起始位置插入source_string的字符序列。

 SELECT ID, abc = STUFF ( (SELECT ',' +Name FROM temp1 FOR XML PATH('')), 1, 1, '' ) FROM temp1 GROUP by iD Output: ----------------------------------- | Id | Name | |---------------------------------| | 1 | aaa,bbb,ccc,ddd,eee | ----------------------------------- 

Azure SQL数据库和SQL Server中有非常新的功能(从2017开始)来处理这种确切的情况。 我相信这将作为您正在尝试使用XML / STUFF方法完成的本地官方方法。 例:

 select id, STRING_AGG(name, ',') as abc from temp1 group by id 

STRING_AGG – https://msdn.microsoft.com/en-us/library/mt790580.aspx

编辑:当我最初发布这个,我提到了SQL Server 2016,因为我以为我看到了一个潜在的功能,将被列入。 要么我记得不正确或改变了什么,谢谢你提供的修改版本。 此外,相当深刻的印象,并没有完全意识到多步骤审查过程,只是拉我最后的选择。

在for xml路径中如果我们定义任何值,如[for xml path('ENVLOPE')]那么

上面的标签将被添加到每一行。

 Declare @Temp As Table (Id Int,Name Varchar(100)) Insert Into @Temp values(1,'A'),(1,'B'),(1,'C'),(2,'D'),(2,'E'),(3,'F'),(3,'G'),(3,'H'),(4,'I'),(5,'J'),(5,'K') Select X.ID, stuff((Select ','+ Z.Name from @Temp Z Where X.Id =Z.Id For XML Path('')),1,1,'') from @Temp X Group by X.ID 
 SELECT ID, abc = STUFF( (SELECT ',' + name FROM temp1 FOR XML PATH ('')), 1, 1, '' ) FROM temp1 GROUP BY id 

在上面的查询中, STUFF函数用于从生成的xml字符串(,aaa,bbb,ccc,ddd,eee)删除第一个逗号(,) (,aaa,bbb,ccc,ddd,eee)那么它将成为(aaa,bbb,ccc,ddd,eee)

FOR XML PATH('')只是将列数据转换为(,aaa,bbb,ccc,ddd,eee)字符串,但是在PATH中,我们传递'',所以它不会创建XML标记。

最后,我们使用ID列对记录进行分组。

我做了调试,并最终返回到我的'填充'查询这是正常的方式。

只是

 select * from myTable for xml path('myTable') 

给我表中的内容从我调试的触发器写入日志表。