65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (17投票s)

2010年1月26日

CPOL

3分钟阅读

viewsIcon

118552

描述了在使用 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 时,请使用(相关)子查询。

© . All rights reserved.