在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')
给我表中的内容从我调试的触发器写入日志表。