简介For XML PATH
For XML,就是将查询结果作为XML来检索。
XML有4中模式,官方文档上有这4种模式的使用方法。
模式 | 描述 |
RAW | SELECT 语句返回的行集中为每个行生成一 <个行> 元素 |
AUTO | 将基于指定 SELECT 语句的方式来使用试探性方法在 XML 结果中生成嵌套。(说的是什么?是能够生成嵌套的XML吗?) |
EXPICIT | 模式允许对 XML 的形状进行更多控制。 |
PATH | 任务比较简单的方式。 |
仅当执行设置了这些模式的查询时,这些模式才有效。 它们不会影响以后执行的任何查询的结果。
接下来我们只讲Path模式(事实上,我在网上看到最多的也是PATH模式的例子)。
很多问题等遇上了,再说,先说说FOR XML PATH的基本用法。
假设有一张兴趣爱好表(t_bd_hobby),表内容如下:
接下来,我们执行FOR XML语句:
SELECT *
FROM dbo.t_bd_hobby
FOR XML PATH
返回结果如下
1 <row>
2 <ID>1</ID>
3 <hobby>爬山</hobby>
4 </row>
5 <row>
6 <ID>2</ID>
7 <hobby>游泳</hobby>
8 </row>
9 <row>
10 <ID>3</ID>
11 <hobby>美食</hobby>
12 </row>
因此,FOR XML PATH可以将查询结果依据行输出成XML格式。
自定义节点名称
如何改变节点的名称呢?
SELECT *
FROM dbo.t_bd_hobby
FOR XML PATH('Myhobby')
输出的XML如下:
1 <Myhobby>
2 <ID>1</ID>
3 <hobby>爬山</hobby>
4 </Myhobby>
5 <Myhobby>
6 <ID>2</ID>
7 <hobby>游泳</hobby>
8 </Myhobby>
9 <Myhobby>
10 <ID>3</ID>
11 <hobby>美食</hobby>
12 </Myhobby>
当然,我们也可以自定义列名称。
SELECT ID AS 编码, hobby AS 兴趣
FROM dbo.t_bd_hobby
FOR XML PATH
输出的XML如下:
1 <row>
2 <编码>1</编码>
3 <兴趣>爬山</兴趣>
4 </row>
5 <row>
6 <编码>2</编码>
7 <兴趣>游泳</兴趣>
8 </row>
9 <row>
10 <编码>3</编码>
11 <兴趣>美食</兴趣>
12 </row>
For XML Path的应用场景——多行转一行
既然节点和列都可以被自定义,那么我们是不是可以构建我们喜欢的输出方式呢?
SELECT '[ '+hobby+' ]' FROM t_bd_hobby FOR XML PATH('')
输出如下:
[ 游泳 ][ 爬山 ][ 美食 ]
可以改成用逗号分割
SELECT ',' + hobby FROM t_bd_hobby FOR XML PATH('')
这样,多行合并为一行,每个字段用逗号隔开。
,游泳,爬山,美食,
有没有一个函数截取字符串,把前面一个逗号去掉?这样就可以转换为我们常见的形式。stuff就是这么用的。
SELECT stuff( ( SELECT ',' + hobby FROM t_bd_hobby FOR XML PATH('')) ,1,1 ,'')
输出结果如下:
游泳,爬山,美食
For XML Path的应用场景——分组的多行转一行
既然我们能够将多行合并为一行,那么我们能不能完成如下功能。首先有一张学生表,数据如下:
我们要以名字为列,将爱好按逗号隔开,如下所示:
该如何做呢?执行语句的总体思想如下:
1. 按照fname分类。所以使用grou by语句。
2、使用上文已知的字符串截取方法,将fhobby视为被分组的聚合函数,这里先用1表示。
SELECT fname , '1' AS hobby
FROM tb_personhobbys
GROUP BY fname
显示结果如下:
3.没问题后,我们将'1'替换为for xml path的多行转一行的形式。
1 SELECT fname , (STUFF(( SELECT + ',' + fhobby FROM tb_personhobbys FOR XML PATH('')) ,1,1, '' )) AS hobby
2 FROM tb_personhobbys
3 GROUP BY fname
这样的结果,将所有的hobby都传达进来了。
显然不是我们想要的结果,能将hobby局行列传递条件吗?当然可以。
3. 聚合列内传递fname条件。这里,为了区分,需要给外面的表(tb_personhobbys)命名。
1 SELECT fname , (STUFF(( SELECT + ',' + fhobby FROM tb_personhobbys WHERE fname = a.fname FOR XML PATH('')) ,1,1, '' )) AS hobby
2 FROM tb_personhobbys a
3 GROUP BY fname
这样,我们就完成了分组的多行转一行了。
转义字符正常显示
有时候我们的数据库中有尖括号,然后有用到xml的时候,sql会自动的把尖括号进行转义,但是我们不需要转义,并且对这个有要求要用到,这时候我们可以用value的形式来实现,测试数据:
--测试数据
if not object_id(N'Tempdb..#T') is null
drop table #T
Go
Create table #T([ID] int,[Chemistry] nvarchar(47))
Insert #T
select 1,N'CaF<sub>2</sub>' union all
select 2,N'Al<sub>2</sub>O<sub>3<?sub>'
Go
--测试数据结束
用for xml path读取指标信息
SELECT
STUFF((
Select ','+Chemistry from #T FOR XML PATH('')),1,1,'')
读取的结果是这样的:
我们看到尖括号已经进行了转义,如果我们还想要尖括号,可以这样写:
SELECT
STUFF((
Select ','+Chemistry from #T FOR XML PATH(''),TYPE
).value('.','NVARCHAR(MAX)')
,1,1,'')
结果:
这样的结果已经是不再转义的了。
参考网址
- https://www.cnblogs.com/gzb1/p/16420291.html
- FOR XML(SQL Server):FOR XML (SQL Server) - SQL Server | Microsoft Learn