在使用 SQL Server 的 FOR XML AUTO 语句时控制 XML 输出






4.91/5 (17投票s)
描述了在使用 FOR XML AUTO 语句时如何更好地控制 XML 输出。例如,当添加 XML 标签时。
引言
本文介绍了如何在使用 FOR XML AUTO
语句时更好地控制 XML 输出。例如,当添加 XML 标签时。而不是使用更难理解的 FOR XML EXPLICIT
语句。 如果您即将反序列化 XML 输出以在应用程序中使用,您可能会发现此信息很有用。
背景
在 FOR XML
子句中,您通常使用以下模式之一
RAW
AUTO
EXPLICIT
PATH
当您想要完全控制生成的 XML 时,您使用 FOR XML EXPLICIT
,但这很难理解、阅读和维护最终复杂的 SELECT
语句。 FOR XML AUTO
生成最易读的 SELECT
语句,但缺点是您对生成的 XML 的控制最少。 但通过一些技巧,有时通过使用 PATH
选项,您可以做超出您期望的事情。 RAW
选项很少使用,因此不讨论。 PATH
选项允许您更轻松地混合属性和元素。 现在,我们使用 FOR XML AUTO
。
这个例子:
对于本例,我们在一个数据库中使用两个简单的表,它们具有 1:N 的关系。 一个表 (SalesOrder) 包含订单以及客户信息,另一个表 (Items) 包含项目。 一个订单可以有多个项目,一个项目始终属于一个订单。
代码
我总是喜欢从简单的开始。 所以
SELECT * FROM salesorder
产生:
ordernumber customername customerstreet
----------- ------------ --------------
1 parker first av
2 lesley sec av
如果我们要结果集为 XML,我们添加 FOR XML AUTO
语句
SELECT * FROM salesorder FOR XML AUTO
它产生:
<salesorder ordernumber="1" customername="parker" customerstreet="first av"/>
<salesorder ordernumber="2" customername="lesley" customerstreet="sec av"/>
但是现在,您将字段作为属性。 大部分情况下,您希望它们作为元素。 为此,您添加 ELEMENTS
参数
SELECT * FROM salesorder FOR XML AUTO, ELEMENTS
产生:
<salesorder>
<ordernumber>1</ordernumber>
<customername>parker</customername>>
<customerstreet>first av</customerstreet>
</salesorder>
<salesorder>
<ordernumber>2</ordernumber>
<customername>lesley</customername>
<customerstreet>sec av</customerstreet>
</salesorder>
如果要更改 'salesorder
' 标签,您可以使用
SELECT * FROM salesorder AS niceorder FOR XML AUTO, ELEMENTS
生成结果如下
<niceorder>
<ordernumber>1</ordernumber>
<customername>parker</customername>>
<customerstreet>first av</customerstreet>
</niceorder>
<niceorder>
<ordernumber>2</ordernumber>
<customername>lesley</customername>
<customerstreet>sec av</customerstreet>
</niceorder>
当然,这个技巧也适用于列名
SELECT ordernumber AS order_no FROM salesorder WHERE ordernumber = 1 FOR XML AUTO, ELEMENTS
生成结果如下
<salesorder>
<order_no>1</order_no >
</salesorder>
如果您想添加额外的标签或添加节点怎么办? 例如,在客户信息周围添加 'customer' 标签? 这被证明使用 FOR XML AUTO
是困难的。 一个可能的解决方案是 SELF JOIN
(连接到同一张表),但我发现了一些更容易的事情。 经过反复试验和修补,我们使用子查询并略微滥用 FOR XML PATH
命令,如下所示
SELECT
ordernumber,
(SELECT customername ,
customerstreet FOR XML PATH(''),
TYPE, ELEMENTS)
as customer
FROM
salesorder
FOR XML AUTO, ELEMENTS
生成结果如下
<salesorder>
<ordernumber>1</ordernumber>
<customer>
<customername>parker</customername>
<customerstreet>first av</customerstreet>
</customer>
</salesorder>
<salesorder>
<ordernumber>2</ordernumber>
<customer>
<customername>lesley</customername>
<customerstreet>sec av</customerstreet>
</customer>
</salesorder>
请注意使用附加的 'TYPE
' 参数。 这将确保子查询的结果作为 XML 类型返回(作为整个 XML 类型结果的一部分),而不是 NVARCHAR(MAX)
类型。 如果您想向完整结果添加周围标签,这个巧妙的小技巧也适用
SELECT(
SELECT
customername
FROM
salesorder
FOR XML AUTO, TYPE, ELEMENTS
) AS orderrequest FOR XML PATH(''), TYPE, ELEMENTS
生成结果如下
<orderrequest>
<salesorder>
<customername>parker</customername>
</salesorder>
<salesorder>
<customername>lesley</customername>
</salesorder>
</orderrequest>
为什么我们不在子查询中使用标准的 FOR XML AUTO
? 试一试,它会产生一个错误。 只有当子查询是对实际表的查询时,您才能在子查询中使用 FOR XML AUTO
(上述情况显然不是)。
如果您想完全控制生成的 XML 输出,子查询结构是正确的。 假设我们希望,每个订单都有客户姓名和属于该订单的所有项目。 为此,您可以使用相关子查询,如下所示
SELECT
customername ,
(SELECT * FROM item WHERE item.ordernumber =
salesorder.ordernumber FOR XML AUTO, TYPE, ELEMENTS)
FROM
salesorder
FOR XML AUTO, ELEMENTS
生成结果如下
<salesorder>
<customername>parker</customername>
<item>
<itemnumber>10</itemnumber>
<description>pen</description>
<ordernumber>1</ordernumber>
</item>
<item>
<itemnumber>11</itemnumber>
<description>paper</description>
<ordernumber>1</ordernumber>
</item>
</salesorder>
当使用相关子查询时,您可以使用常规的 FOR XML AUTO, TYPE, ELEMENTS
语句。 如果您想在 'items' 周围添加标签,只需在子查询后添加 AS
,如下所示
SELECT
customername ,
(SELECT * FROM item WHERE item.ordernumber =
salesorder.ordernumber FOR XML AUTO, TYPE, ELEMENTS)
AS orderitems
FROM
salesorder
FOR XML AUTO, ELEMENTS
生成结果如下
<salesorder>
<customername>parker</customername>
<orderitems>
<item>
<itemnumber>10</itemnumber>
<description>pen</description>
<ordernumber>1</ordernumber>
</item>
<item>
<itemnumber>11</itemnumber>
<description>paper</description>
<ordernumber>1</ordernumber>
</item>
</orderitems>
</salesorder>
为什么我们不简单地将项目表连接到订单表? 当涉及到生成的 XML 的布局时,这有时会导致不希望的、不可预测的行为。 例如
SELECT
item.description,
salesorder.customername
FROM
salesorder
INNER JOIN item ON item.ordernumber = salesorder.ordernumber
FOR XML AUTO, ELEMENTS
将产生这样的垃圾
<item>
<description>pen</description>
<salesorder>
<customername>parker</customername>
</salesorder>
</item>
<item>
<description>paper</description>
<salesorder>
<customername>parker</customername>
</salesorder>
</item>
此外,当使用连接时,您将很难添加周围标签。 因此,对于大多数控制,当使用 FOR XML AUTO
时,请使用(相关)子查询。