SQL Server : FOR XML PATH 函数用法以及转义字符正常显示

简介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

这样,我们就完成了分组的多行转一行了。

参考网址

posted @ 2022-08-05 20:20  陆陆无为而治者  阅读(876)  评论(0编辑  收藏  举报