使用 EXPLICIT 模式

如主题使用 FOR XML 构造 XML 中所述,使用 RAW 和 AUTO 模式不能很好地控制从查询结果生成的 XML 的形状。但是,对于要从查询结果生成 XML,EXPLICIT 模式会提供非常好的灵活性。

必须以特定的方式编写 EXPLICIT 模式查询,以便将有关所需的 XML 的附加信息(如 XML 中的所需嵌套)显式指定为查询的一部分。根据所请求的 XML,编写 EXPLICIT 模式查询可能会很烦琐。您会发现使用 PATH 模式(具有嵌套)相对编写 EXPLICIT 模式查询而言更加简单。

因为将所需的 XML 描述为 XPLICIT 模式查询的一部分,所以必须确保生成的 XML 格式正确且有效。

EXPLICIT 模式下的行集处理

EXPLICIT 模式会将由查询执行生成的行集转换为 XML 文档。为使 EXPLICIT 模式生成 XML 文档,行集必须具有特定的格式。这需要您编写 SELECT 查询以生成具有特定格式的行集(通用表),以便处理逻辑随后可以生成所需的 XML。

首先,查询必须生成下列两个元数据列:

  • 第一列必须提供当前元素的标记号(整数类型),并且列名必须是 Tag。查询必须为从行集构造的每个元素提供唯一标记号。
  • 第二列必须提供父元素的标记号,并且此列的列名必须是 Parent。这样,Tag 和 Parent 列将提供层次结构信息。

这些元数据列值与列名中的信息一起都用于生成所需的 XML。请注意,查询必须以特定的方式提供列名。同时请注意,Parent 列中的 0 或 NULL 表明相应的元素没有父级。该元素将作为顶级元素添加到 XML。

为了了解如何将查询生成的通用表处理为生成 XML 结果,假定您已经编写了一个生成此通用表的查询:

通用表示例

注意有关此通用表的以下事项:

  • 前两列是 TagParent,它们是元数据列。这些值确定层次结构。
  • 列名是以特定的方式指定的,本主题的后面部分将进行介绍。
  • 从此通用表生成 XML 的过程中,此表中的数据被垂直分区到列组中。分组是根据 Tag 值和列名确定的。在构造 XML 的过程中,处理逻辑为每行选择一组列,然后构造一个元素。在此示例中,将应用以下规则:
    • 对于第一行中的 Tag 列值 1,名称中包括此相同标记号的列(Customer!1!cidCustomer!1!name)形成一组。这些列用于处理行,您可能已经注意到所生成元素的形式为 <Customer id=... name=...>。列名格式在本主题的后面部分进行介绍。
    • 对于 Tag 列值为 2 的行,列 Order!2!idOrder!2!date 形成一组,然后该组用于构造元素(形式为 <Order id=... date=... />)。
    • 对于 Tag 列值为 3 的行,列 OrderDetail!3!id!idOrderDetail!3!pid!idref 形成一组。其中每一行都从这些列生成一个元素(形式为 <OrderDetail id=... pid=...>)。
  • 请注意,在生成 XML 层次结构的过程中,行是按顺序处理的。XML 层次结构确定如下:
    • 第一行指定 Tag 值为 1,Parent 值为 NULL。因此,相应的元素(即 <Customer> 元素)将作为顶级元素添加在 XML 中。

      <Customer cid="C1" name="Janine">
      
    • 第二行确定 Tag 值为 2,Parent 值为 1。因此,<Order> 元素将作为 <Customer> 元素的子元素添加。

      <Customer cid="C1" name="Janine">
         <Order id="O1" date="1/20/1996">
      
    • 下两行确定 Tag 值为 3,Parent 值为 2。因此,两个 <OrderDetail> 元素将作为 <Order> 元素的子元素添加。

      <Customer cid="C1" name="Janine">
         <Order id="O1" date="1/20/1996">
            <OrderDetail id="OD1" pid="P1"/>
            <OrderDetail id="OD2" pid="P2"/>
      
    • 最后一行确定 Tag 号为 2,Parent 标记号为 1。因此,另一个 <Order> 元素将作为 <Customer> 父元素的子元素添加。

      <Customer cid="C1" name="Janine">
         <Order id="O1" date="1/20/1996">
            <OrderDetail id="OD1" pid="P1"/>
            <OrderDetail id="OD2" pid="P2"/>
         </Order>
         <Order id="O2" date="3/29/1997">
      </Customer>
      

简言之,使用 EXPLICIT 模式时,TagParent 元数据列中的值、列名中提供的信息以及正确的行顺序将生成所需的 XML。

通用表行顺序

在构造 XML 过程中,通用表中的行是按顺序处理的。因此,若要检索到与其父级关联的正确的子级实例,必须对行集中的行进行排序,以便每个父节点后紧跟着其子节点。

指定通用表中的列名

在编写 EXPLICIT 模式查询时,必须使用以下格式指定所得到的行集中的列名。它们提供转换信息(包括元素名称和属性名称)以及用指令指定的其他附加信息。

常用格式如下:

ElementName!TagNumber!AttributeName!Directive

下面是对格式各部分的说明。

  • ElementName
    是所生成元素的通用标识符。例如,如果将 ElementName 指定为 Customers,将生成 <Customers> 元素。
  • TagNumber
    是分配给元素的唯一标记值。在两个元数据列(TagParent)的帮助下,此值将确定所得到的 XML 中的元素的嵌套。
  • AttributeName
    提供要在指定的 ElementName 中构造的属性的名称。如果没有指定 Directive,将发生这种行为。

    如果指定了 Directive 并且它是 xmlcdataelement,则此值用于构造 ElementName 的子元素,并且此列值将添加到该子元素。

    如果指定了 Directive,则 AttributeName 可以为空。例如 ElementName!TagNumber!!Directive。在这种情况下,列值直接由 ElementName 包含。

  • Directive
    Directive 是可选的,可以使用它来提供有关 XML 构造的其他信息。Directive 有两种用途。

    一种用途是将值编码为 ID、IDREF 和 IDREFS。可以将 IDIDREFIDREFS 关键字指定为 Directives。这些指令将覆盖属性类型。这使您能够创建文档内链接。

    同时,可以使用 Directive 来指示如何将字符串数据映射到 XML。可以将 hideelement、elementxsinilxmlxmltextcdata 关键字用作 Directivehide 指令会隐藏节点。当仅为排序目的而检索值,但又不想让它们出现在所得到的 XML 中时,此指令非常有用。

    element 指令生成的结果中包含元素而不是属性。包含的数据被编码为实体。例如,< 字符变成 &lt;。对于 NULL 列值,不会生成任何元素。如果要为 NULL 列值生成元素,可以指定 elementxsinil 指令。这将生成具有属性 xsi:nil=TRUE 的元素。

    除不发生实体编码外,xml 指令与 element 指令相同。请注意,可以将 element 指令与 IDIDREFIDREFS 结合使用,然而不允许 xml 指令与除 hide 指令之外的任何其他指令结合使用。

    cdata 指令通过将数据与 CDATA 部分包装在一起来包含数据。不对内容进行实体编码。原始数据类型必须是文本类型(如 varcharnvarchartextntext)。此指令只适用于 hide。当使用此指令时,不能指定 AttributeName

    大多数情况下,允许在这两个组之间组合指令,但不允许在它们自身当中组合指令。

    如果未指定 DirectiveAttributeName(例如 Customer!1),则暗含一个 element 指令(如 Customer!1!!element),并且列数据包含在 ElementName 中。

    如果指定了 xmltext 指令,则列内容包装在与文档的其余部分集成在一起的单个标记中。在提取由 OPENXML 存储在列中的溢出(未用完的)XML 数据时,此指令很有用。有关详细信息,请参阅使用 OPENXML 查询 XML

    如果指定了 AttributeName,将由指定的名称替换标记名。否则,将通过把内容置于包容的起始位置(不经过实体编码)将属性追加到闭合元素属性的当前列表中。包含此指令的列必须是文本类型(如 varcharnvarcharcharnchartextntext)。此指令只适用于 hide。在提取列中存储的溢出数据时,此指令很有用。如果内容的 XML 格式不正确,则未定义该行为。

示例

下面是说明如何使用 EXPLICIT 模式的一些示例。

A. 检索雇员信息

此示例检索每个雇员的雇员 ID 和雇员姓名。在 AdventureWorks 数据库中,可从 Employee 表中获得 EmployeeID。可从 Contact 表中获得雇员姓名。可使用 ContactID 列来联接表。

假定要让 FOR XML EXPLICIT 转换生成 XML,如下所示:

<Employee EmpID="1" >
  <Name FName="Guy" LName="Gilbert" />
</Employee>
...

因为层次结构中有两个级别,所以应编写两个 SELECT 查询并应用 UNION ALL。下面是检索 <Employee> 元素及其属性的值的第一个查询。该查询为 <Employee> 元素的 Tag 值赋予 1,为 Parent 赋予 NULL,因为它是一个顶级元素。

SELECT 1    as Tag,
       NULL as Parent,
       EmployeeID as [Employee!1!EmpID],
       NULL       as [Name!2!FName],
       NULL       as [Name!2!LName]
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID

下面是第二个查询。它检索 <Name> 元素的值。它为 <Name> 元素的 Tag 值赋予 2,为 Parent 标记值赋予 1,从而将 <Employee> 标识为父元素。

SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       FirstName, 
       LastName 
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID

可以使用 UNION ALL 组合这些查询,应用 FOR XML EXPLICIT,并指定所需的 ORDER BY 子句。必须首先按 EmployeeID 然后按姓名对行集进行排序,以便首先显示姓名中的 NULL 值。通过执行以下不带 FOR XML 子句的查询,可以看到会生成通用表。

下面是最终查询:

SELECT 1    as Tag,
       NULL as Parent,
       EmployeeID as [Employee!1!EmpID],
       NULL       as [Name!2!FName],
       NULL       as [Name!2!LName]
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       FirstName, 
       LastName 
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
ORDER BY [Employee!1!EmpID],[Name!2!FName]
FOR XML EXPLICIT

下面是部分结果:

<Employee EmpID="1">
  <Name FName="Guy" LName="Gilbert" />
</Employee>
<Employee EmpID="2">
  <Name FName="Kevin" LName="Brown" />
</Employee>
...

第一个 SELECT 指定所得到的行集中的列名。这些名称形成两个列组。列名中 Tag 值为 1 的组将 Employee 标识为元素,将 EmpID 标识为属性。另一个列组的列中的 Tag 值为 2,它将 <Name> 标识为元素,将 FName 和 LName 标识为属性。

下表显示了查询生成的部分行集:

Tag Parent  Employee!1!EmpID Name!2!FName Name!2!LName
----------- ----------- ---------------- -------------------
1    NULL     1                NULL          NULL
2     1       1                Guy           Gilbert
1    NULL     2                NULL          NULL
2     1       2                Kevin         Brown
1    NULL     3                NULL          NULL
2     1       3                Roberto       Tamburello 
...

下面说明了如何处理通用表中的行以生成所得到的 XML 树:

第一行将 Tag 值标识为 1。因此,将标识列名 (Employee!1!EmpID) 中 Tag 值为 1 的列组。此列将 Employee 标识为元素名称。然后,将创建一个带有 EmpID 属性的 <Employee> 元素。将为这些属性分配相应的列值。

第二行的 Tag 值为 2。因此,将标识列名(Name!2!FName 和 Name!2!LName)中 Tag 值为 2 的列组。这些列名将 Name 标识为元素名称。将创建一个带有 FName 和 LName 属性的 <Name> 元素。然后,将为这些属性分配相应的列值。此行将 Parent 标识为 1。此元素将作为前面所创建的 <Employee> 元素的子元素添加。

对于行集中的其余行,重复此过程。注意对通用表中的行进行排序以使 FOR XML EXPLICIT 可以按顺序处理行集并生成所需的 XML 的重要性。

B. 指定元素指令

此示例与示例 A 类似,只是此示例生成以元素为中心的 XML,如下所示:

<Employee EmpID=...>
  <Name>
    <FName>...</FName>
    <LName>...</LName>
  </Name>
</Employee>

除在列名中添加了 ELEMENT 指令外,该查询将与示例 A 中的查询保持相同。因此,将 <FName> 和 <LName> 元素作为 <Name> 元素的子元素添加,而不是作为属性添加。因为 Employee!1!EmpID 列没有指定 ELEMENT 指令,所以将 EmpID 作为 <Employee> 元素的属性添加。

SELECT 1 as Tag,
       NULL as Parent,
       EmployeeID as [Employee!1!EmpID],
       NULL       as [Name!2!FName!ELEMENT],
       NULL       as [Name!2!LName!ELEMENT]
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       FirstName, 
       LastName 
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
ORDER BY [Employee!1!EmpID],[Name!2!FName!ELEMENT]
FOR XML EXPLICIT

下面是部分结果

<Employee EmpID="1">
  <Name>
    <FName>Guy</FName>
    <LName>Gilbert</LName>
  </Name>
</Employee>
<Employee EmpID="2">
  <Name>
    <FName>Kevin</FName>
    <LName>Brown</LName>
  </Name>
</Employee>
...

C. 指定 elementxsinil 指令

当指定 ELEMENT 指令检索以元素为中心的 XML 时,如果列具有 NULL 值,则 EXPLICIT 模式将不生成相应的元素。另外,也可以指定 ELEMENTXSINIL 指令以请求为 xsi:nil 属性设置为 TRUE 值的 NULL 值生成元素。

以下查询构造包括雇员地址的 XML。对于 AddressLine2 和 City 列,列名指定 ELEMENTXSINIL 指令。这将为行集中的 AddressLine2 和 City 列中的 NULL 值生成元素。

SELECT 1    as Tag,
       NULL as Parent,
       EmployeeID  as [Employee!1!EmpID],
       E.AddressID as [Employee!1!AddressID],
       NULL        as [Address!2!AddressID],
       NULL        as [Address!2!AddressLine1!ELEMENT],
       NULL        as [Address!2!AddressLine2!ELEMENTXSINIL],
       NULL        as [Address!2!City!ELEMENTXSINIL]
FROM   HumanResources.EmployeeAddress E, Person.Address A
WHERE  E.ContactID = A.ContactID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       E.AddressID,
       A.AddressID,
       AddressLine1, 
       AddressLine2,
       City 
FROM   HumanResources.EmployeeAddress E, Person.Address A
WHERE  E.AddressID = A.AddressID
ORDER BY [Employee!1!EmpID],[Address!2!AddressID]
FOR XML EXPLICIT

下面是部分结果:

<Employee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        EmpID="1" AddressID="61">
  <Address AddressID="61">
    <AddressLine1>7726 Driftwood Drive</AddressLine1>
    <AddressLine2 xsi:nil="true" />
    <City>Monroe</City>
  </Address>
</Employee>
...

D. 使用 EXPLICIT 模式构造同级

假定要构造提供销售订单信息的 XML。请注意,<SalesPerson> 和 <OrderDetail> 元素是同级。每个订单都有一个 <OrderHeader> 元素、一个 <SalesPerson> 元素和一个或多个 <OrderDetail> 元素。

<OrderHeader SalesOrderID=... OrderDate=... CustomerID=... >
  <SalesPerson SalesPersonID=... />
  <OrderDetail SalesOrderID=... LineTotal=... ProductID=... OrderQty=... />
  <OrderDetail SalesOrderID=... LineTotal=... ProductID=... OrderQty=.../>
      ...
</OrderHeader>
<OrderHeader ...</OrderHeader>

以下 EXPLICIT 模式查询将构造此 XML。请注意,查询将 <OrderHeader> 元素的 Tag 值指定为 1,将 <SalesPerson> 元素的此值指定为 2,将 <OrderDetail> 元素的此值指定为 3。因为 <SalesPerson> 和 <OrderDetail> 是同级,所以该查询将 Parent 标记值都指定为 1,从而标识 <OrderHeader> 元素。

SELECT  1 as Tag,
        0 as Parent,
        SalesOrderID  as [OrderHeader!1!SalesOrderID],
        OrderDate     as [OrderHeader!1!OrderDate],
        CustomerID    as [OrderHeader!1!CustomerID],
        NULL          as [SalesPerson!2!SalesPersonID],
        NULL          as [OrderDetail!3!SalesOrderID],
        NULL          as [OrderDetail!3!LineTotal],
        NULL          as [OrderDetail!3!ProductID],
        NULL          as [OrderDetail!3!OrderQty]
FROM   Sales.SalesOrderHeader
WHERE     SalesOrderID=43659 or SalesOrderID=43661
UNION ALL 
SELECT 2 as Tag,
       1 as Parent,
        SalesOrderID,
        NULL,
        NULL,
        SalesPersonID,  
        NULL,         
        NULL,         
        NULL,
        NULL         
FROM   Sales.SalesOrderHeader
WHERE     SalesOrderID=43659 or SalesOrderID=43661
UNION ALL
SELECT 3 as Tag,
       1 as Parent,
        SOD.SalesOrderID,
        NULL,
        NULL,
        SalesPersonID,
        SOH.SalesOrderID,
        LineTotal,
        ProductID,
        OrderQty   
FROM    Sales.SalesOrderHeader SOH,Sales.SalesOrderDetail SOD
WHERE   SOH.SalesOrderID = SOD.SalesOrderID
AND     (SOH.SalesOrderID=43659 or SOH.SalesOrderID=43661)
ORDER BY [OrderHeader!1!SalesOrderID], [SalesPerson!2!SalesPersonID],
         [OrderDetail!3!SalesOrderID],[OrderDetail!3!LineTotal]
FOR XML EXPLICIT

下面是部分结果:

<OrderHeader SalesOrderID="43659" OrderDate="2001-07-01T00:00:00" CustomerID="676">
  <SalesPerson SalesPersonID="279" />
  <OrderDetail SalesOrderID="43659" LineTotal="10.373000" ProductID="712" OrderQty="2" />
  <OrderDetail SalesOrderID="43659" LineTotal="28.840400" ProductID="716" OrderQty="1" />
  <OrderDetail SalesOrderID="43659" LineTotal="34.200000" ProductID="709" OrderQty="6" />
   ...
</OrderHeader>
<OrderHeader SalesOrderID="43661" OrderDate="2001-07-01T00:00:00" CustomerID="442">
  <SalesPerson SalesPersonID="282" />
  <OrderDetail SalesOrderID="43661" LineTotal="20.746000" ProductID="712" OrderQty="4" />
  <OrderDetail SalesOrderID="43661" LineTotal="40.373000" ProductID="711" OrderQty="2" />
   ...
</OrderHeader>

E. 指定 ID 和 IDREF 指令

此示例与示例 C 基本相同,唯一的区别在于该查询指定了 ID 和 IDREF 指令。这些指令将覆盖 <OrderHeader> 和 <OrderDetail> 元素中 SalesPersonID 属性的类型。这将形成文档内链接。需要使用架构来查看被覆盖的类型。因此,该查询将在 FOR XML 子句中指定 XMLDATA 选项来检索架构。

SELECT  1 as Tag,
        0 as Parent,
        SalesOrderID  as [OrderHeader!1!SalesOrderID!id],
        OrderDate     as [OrderHeader!1!OrderDate],
        CustomerID    as [OrderHeader!1!CustomerID],
        NULL          as [SalesPerson!2!SalesPersonID],
        NULL          as [OrderDetail!3!SalesOrderID!idref],
        NULL          as [OrderDetail!3!LineTotal],
        NULL          as [OrderDetail!3!ProductID],
        NULL          as [OrderDetail!3!OrderQty]
FROM   Sales.SalesOrderHeader
WHERE  SalesOrderID=43659 or SalesOrderID=43661
UNION ALL 
SELECT 2 as Tag,
       1 as Parent,
        SalesOrderID, 
        NULL,
        NULL,
        SalesPersonID,  
        NULL,         
        NULL,         
        NULL,
        NULL         
FROM   Sales.SalesOrderHeader
WHERE  SalesOrderID=43659 or SalesOrderID=43661
UNION ALL
SELECT 3 as Tag,
       1 as Parent,
        SOD.SalesOrderID,
        NULL,
        NULL,
        SalesPersonID,
        SOH.SalesOrderID,
        LineTotal,
        ProductID,
        OrderQty   
FROM    Sales.SalesOrderHeader SOH,Sales.SalesOrderDetail SOD
WHERE   SOH.SalesOrderID = SOD.SalesOrderID
AND     (SOH.SalesOrderID=43659 or SOH.SalesOrderID=43661)
ORDER BY [OrderHeader!1!SalesOrderID!id], [SalesPerson!2!SalesPersonID],
         [OrderDetail!3!SalesOrderID!idref],[OrderDetail!3!LineTotal]
FOR XML EXPLICIT, XMLDATA

下面是部分结果。请注意,在架构中,IDIDREF 指令已经覆盖了 <OrderHeader> 和 <OrderDetail> 元素中 SalesOrderID 属性的数据类型。如果删除这些指令,架构将返回这些属性的原始类型。

<Schema name="Schema1" xmlns="urn:schemas-microsoft-com:xml-data" xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <ElementType name="OrderHeader" content="mixed" model="open">
    <AttributeType name="SalesOrderID" dt:type="id" />
    <AttributeType name="OrderDate" dt:type="dateTime" />
    <AttributeType name="CustomerID" dt:type="i4" />
    <attribute type="SalesOrderID" />
    <attribute type="OrderDate" />
    <attribute type="CustomerID" />
  </ElementType>
  <ElementType name="SalesPerson" content="mixed" model="open">
    <AttributeType name="SalesPersonID" dt:type="i4" />
    <attribute type="SalesPersonID" />
  </ElementType>
  <ElementType name="OrderDetail" content="mixed" model="open">
    <AttributeType name="SalesOrderID" dt:type="idref" />
    <AttributeType name="LineTotal" dt:type="number" />
    <AttributeType name="ProductID" dt:type="i4" />
    <AttributeType name="OrderQty" dt:type="i2" />
    <attribute type="SalesOrderID" />
    <attribute type="LineTotal" />
    <attribute type="ProductID" />
    <attribute type="OrderQty" />
  </ElementType>
</Schema>
<OrderHeader xmlns="x-schema:#Schema1" SalesOrderID="43659" OrderDate="2001-07-01T00:00:00" CustomerID="676">
  <SalesPerson SalesPersonID="279" />
  <OrderDetail SalesOrderID="43659" LineTotal="10.373000" ProductID="712" OrderQty="2" />
  ...
</OrderHeader>
...

F. 指定 ID 和 IDREFS 指令

可以将元素属性指定为 ID 类型属性,然后可以使用 IDREFS 属性来引用它。这将启用文档内链接,这与关系数据库中主键和外键关系类似。

此示例说明如何使用 IDIDREFS 指令来创建 IDIDREFS 类型的属性。因为 ID 不能是整数值,所以此示例中的 ID 值被转换。也就是说,它们是转换后的类型。ID 值中使用了前缀。

假定要如下所示构造 XML:

<Customer CustomerID="C1" SalesOrderIDList=" O11 O22 O33..." >
    <SalesOrder SalesOrderID="O11" OrderDate="..." />
    <SalesOrder SalesOrderID="O22" OrderDate="..." />
    <SalesOrder SalesOrderID="O33" OrderDate="..." />
    ...
</Customer>

< Customer > 元素的 SalesOrderIDList 属性是一个多值属性,它引用 < SalesOrder > 元素的 SalesOrderID 属性。若要建立此链接,必须将 SalesOrderID 属性声明为 ID 类型,并且必须将 < Customer> 元素的 SalesOrderIDList 属性声明为 IDREFS 类型。因为一个客户可请求多个订单,所以使用 IDREFS 类型。

IDREFS 也有多个值。因此,必须使用单独的 SELECT 子句来重复使用相同的标记、父级和键列信息。然后,ORDER BY 必须确保组成 IDREFS 值的行的序列成组显示在它们的父元素下。

下面是生成所需的 XML 的查询。该查询使用 IDIDREFS 指令来覆盖列名 (SalesOrder!2!SalesOrderID!ID、Customer!1!SalesOrderIDList!IDREFS) 中的类型。

SELECT  1 as Tag,
        0 as Parent,
        C.CustomerID       [Customer!1!CustomerID],
        NULL               [Customer!1!SalesOrderIDList!IDREFS],
        NULL               [SalesOrder!2!SalesOrderID!ID],
        NULL               [SalesOrder!2!OrderDate]
FROM   Sales.Customer C 
UNION ALL 
SELECT  1 as Tag,
        0 as Parent,
        C.CustomerID,
        'O-'+CAST(SalesOrderID as varchar(10)), 
        NULL,
        NULL
FROM   Sales.Customer C, Sales.SalesOrderHeader SOH
WHERE  C.CustomerID = SOH.CustomerID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
        C.CustomerID,
        NULL,
        'O-'+CAST(SalesOrderID as varchar(10)),
        OrderDate
FROM   Sales.Customer C, Sales.SalesOrderHeader SOH
WHERE  C.CustomerID = SOH.CustomerID
ORDER BY [Customer!1!CustomerID] ,
         [SalesOrder!2!SalesOrderID!ID],
         [Customer!1!SalesOrderIDList!IDREFS]
FOR XML EXPLICIT

G. 使用 hide 指令隐藏所得到的 XML 中的元素和属性

此示例说明如何使用 hide 指令。当希望查询返回一个属性以对该查询所返回的通用表中的行进行排序,但是不希望该属性出现在最终所得到的 XML 文档中时,该指令很有用。

以下查询将构造此 XML:

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>
           <Summary> element from XML stored in CatalogDescription column
    </SummaryDescription>
  </Summary>
</ProductModel>

此查询将生成所需的 XML。此查询标识列名中 Tag 值为 1 和 2 的两个列组。

此查询使用 xml 数据类型的 query() 方法(xml 数据类型)来查询 xml 类型的 CatalogDescription 列,以便检索摘要说明。此查询还使用 xml 数据类型的 value() 方法(xml 数据类型)从 CatalogDescription 列中检索 ProductModelID 值。所得到的 XML 中不需要此值,但对生成的行集进行排序时需要此值。因此,列名 [Summary!2!ProductModelID!hide] 包括 hide 指令。如果 SELECT 语句中不包括此列,将不得不按 [ProductModel!1!ProdModelID] 和 [Summary!2!SummaryDescription] 对 xml 类型的行集进行排序,并且不能在 ORDER BY 中使用 xml 类型列。因此,将添加另一个 [Summary!2!ProductModelID!hide] 列,然后在 ORDER BY 子句中指定该列。

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID     as [ProductModel!1!ProdModelID],
        Name               as [ProductModel!1!Name],
        NULL               as [Summary!2!ProductModelID!hide],
        NULL               as [Summary!2!SummaryDescription]
FROM    Production.ProductModel
WHERE   CatalogDescription is not null
UNION ALL
SELECT  2 as Tag,
        1 as Parent,
        ProductModelID,
        Name,
        CatalogDescription.value('
         declare namespace PD="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
       (/PD:ProductDescription/@ProductModelID)[1]', 'int'),
        CatalogDescription.query('
         declare namespace pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
         /pd:ProductDescription/pd:Summary')
FROM    Production.ProductModel
WHERE   CatalogDescription is not null
ORDER BY [ProductModel!1!ProdModelID],[Summary!2!ProductModelID!hide]
FOR XML EXPLICIT
go

结果如下:

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>
      <pd:Summary xmlns:pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription" >
        <p1:p xmlns:p1="http://www.w3.org/1999/xhtml">Our top-of-the-line competition mountain bike. Performance-enhancing options include the innovative HL Frame, super-smooth front suspension, and traction for all terrain. </p1:p>
      </pd:Summary>
    </SummaryDescription>
  </Summary>
</ProductModel>

H. 指定 element 指令和实体编码

此示例说明 elementxml 指令之间的区别。element 指令会实体化数据,但 xml 指令不会。<Summary> 元素是查询中分配的 XML <Summary>This is summary description</Summary>

请看下面的查询:

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID  as [ProductModel!1!ProdModelID],
        Name            as [ProductModel!1!Name],
        NULL            as [Summary!2!SummaryDescription!ELEMENT]
FROM    Production.ProductModel
WHERE   ProductModelID=19
UNION ALL
SELECT  2 as Tag,
        1 as Parent,
        ProductModelID,
        NULL,
       '<Summary>This is summary description</Summary>'
FROM   Production.ProductModel
WHERE  ProductModelID=19
FOR XML EXPLICIT

结果如下。结果中的摘要说明被实体化。

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>&lt;Summary&gt;This is summary description&lt;/Summary&gt;</SummaryDescription>
  </Summary>
</ProductModel>

此时,如果在列名中指定 xml 指令(如 Summary!2!SummaryDescription!xml),而不是指定 element 指令,将收到未经实体化的摘要说明。

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>
      <Summary>This is summary description</Summary>
    </SummaryDescription>
  </Summary>
</ProductModel>

以下查询使用 xml 类型的 query() 方法(而不是分配一个静态 XML 值)从 xml 类型的 CatalogDescription 列中检索产品型号摘要说明。因为已知结果为 xml 类型,所以未应用实体化。

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID  as [ProductModel!1!ProdModelID],
        Name            as [ProductModel!1!Name],
        NULL            as [Summary!2!SummaryDescription]
FROM    Production.ProductModel
WHERE   CatalogDescription is not null
UNION ALL
SELECT  2 as Tag,
        1 as Parent,
        ProductModelID,
        Name,
       (SELECT CatalogDescription.query('
            declare namespace pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
          /pd:ProductDescription/pd:Summary'))
FROM     Production.ProductModel
WHERE    CatalogDescription is not null
ORDER BY [ProductModel!1!ProdModelID],Tag
FOR XML EXPLICIT

I. 指定 cdata 指令

如果指令设置为 cdata,则不对包含的数据进行实体编码,而是将其放入 CDATA 部分。cdata 属性必须没有名称。

以下查询将产品型号摘要说明包装在 CDATA 部分中。

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID  as [ProductModel!1!ProdModelID],
        Name            as [ProductModel!1!Name],
        '<Summary>This is summary description</Summary>'   
            as [ProductModel!1!!cdata] -- no attribute name so ELEMENT assumed
FROM    Production.ProductModel
WHERE   ProductModelID=19
FOR XML EXPLICIT

结果如下:

<ProductModel ProdModelID="19" Name="Mountain-100">
   <![CDATA[<Summary>This is summary description</Summary>]]>
</ProductModel>

J. 指定 xmltext 指令

此示例说明如何在使用 EXPLICIT 模式的 SELECT 语句中使用 xmltext 指令处理溢出列中的数据。

请考察 Person 表。此表含有存储 XML 文档的未用完部分的 Overflow 列。

CREATE TABLE Person(PersonID varchar(5), PersonName varchar(20), Overflow nvarchar(200))
INSERT INTO Person VALUES ('P1','Joe',N'<SomeTag attr1="data">content</SomeTag>')
INSERT INTO Person VALUES ('P2','Joe',N'<SomeTag attr2="data"/>')
INSERT INTO Person VALUES ('P3','Joe',N'<SomeTag attr3="data" PersonID="P">content</SomeTag>')

下面的查询从 Person 表中检索列。对于 Overflow 列,没有指定 AttributeName,但是在提供通用表列名的过程中,将 directive 设置为 xmltext

SELECT 1 as Tag, NULL as parent,
       PersonID as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName],
       overflow as [Parent!1!!xmltext] -- No AttributeName; xmltext directive
FROM Person
FOR XML EXPLICIT

在所得到的 XML 文档中:

  • 因为对于 Overflow 列,没有指定 AttributeName,而指定了 xmltext 指令,所以 <overflow> 元素中的属性被追加到封闭的 <Parent> 元素的属性列表中。
  • 因为 <xmltext> 元素中的 PersonID 属性与相同元素级上检索到的 PersonID 属性冲突,所以忽略 <xmltext> 元素中的此属性,即使 PersonID 为 NULL,也是如此。通常情况下,属性将替代溢出中具有相同名称的属性。

结果如下:

<Parent PersonID="P1" PersonName="Joe" attr1="data">content</Parent>
<Parent PersonID="P2" PersonName="Joe" attr2="data"></Parent>
<Parent PersonID="P3" PersonName="Joe" attr3="data">content</Parent>

如果溢出数据包含子元素且指定了相同的查询,则 Overflow 列中的子元素将作为封闭的 <Parent> 元素的子元素添加。

例如,更改 Person 表中的数据以使 Overflow 列此时包含子元素。

TRUNCATE TABLE Person
INSERT INTO Person VALUES ('P1','Joe',N'<SomeTag attr1="data">content</SomeTag>')
INSERT INTO Person VALUES ('P2','Joe',N'<SomeTag attr2="data"/>')
INSERT INTO Person VALUES ('P3','Joe',N'<SomeTag attr3="data" PersonID="P"><name>PersonName</name></SomeTag>')

如果执行相同的查询,则 <xmltext> 元素中的子元素将作为封闭的 <Parent> 元素的子元素添加。

SELECT 1 as Tag, NULL as parent,
       PersonID as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName],
       overflow as [Parent!1!!xmltext] -- no AttributeName, xmltext directive
FROM Person
FOR XML EXPLICIT

结果如下:

<Parent PersonID="P1" PersonName="Joe" attr1="data">content</Parent>
<Parent PersonID="P2" PersonName="Joe" attr2="data"></Parent>
<Parent PersonID="P3" PersonName="Joe" attr3="data">
  <name>PersonName</name>
</Parent>

如果使用 xmltext 指令指定了 AttributeName,则 <overflow> 元素的属性将作为封闭的 <Parent> 元素的子元素的属性添加。为 AttributeName 指定的名称成为子元素的名称。

在下面的查询中,将 AttributeName (<overflow>) 与 xmltext 指令一起指定*:*

SELECT 1 as Tag, NULL as parent,
       PersonID as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName],
       overflow as [Parent!1!overflow!xmltext] -- overflow is AttributeName
                      -- xmltext is directive
FROM Person
FOR XML EXPLICIT

结果如下:

<Parent PersonID="P1" PersonName="Joe">
  <overflow attr1="data">content</overflow>
</Parent>
<Parent PersonID="P2" PersonName="Joe">
  <overflow attr2="data" />
</Parent>
<Parent PersonID="P3" PersonName="Joe">
  <overflow attr3="data" PersonID="P">
    <name>PersonName</name>
  </overflow>
</Parent>

在下面的查询元素中,为 PersonName 属性指定 directive。这将导致 PersonName 作为封闭的 <Parent> 元素的子元素添加。<xmltext> 的属性仍旧追加到封闭的 <Parent> 元素中。<overflow> 元素、子元素的内容被预置到封闭的 <Parent> 元素的其他子元素中。

SELECT 1      as Tag, NULL as parent,
       PersonID   as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName!element], -- element directive
       overflow   as [Parent!1!!xmltext]
FROM Person
FOR XML EXPLICIT

结果如下:

<Parent PersonID="P1" attr1="data">content<PersonName>Joe</PersonName>
</Parent>
<Parent PersonID="P2" attr2="data">
  <PersonName>Joe</PersonName>
</Parent>
<Parent PersonID="P3" attr3="data">
  <name>PersonName</name>
  <PersonName>Joe</PersonName>
</Parent>

如果 xmltext 列数据包含根元素属性,这些属性将不显示在 XML 数据架构中,并且 MSXML 分析器不验证所得到的 XML 文档片段。例如:

SELECT 1 as Tag,
       0 as Parent,
       N'<overflow a="1"/>' as 'overflow!1!!xmltext'
FOR XML EXPLICIT, xmldata

结果如下。请注意,在返回的架构中缺少溢出属性 a

<Schema name="Schema2" 
        xmlns="urn:schemas-microsoft-com:xml-data" 
        xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <ElementType name="overflow" content="mixed" model="open">
  </ElementType>
</Schema>
<overflow xmlns="x-schema:#Schema2" a="1">
</overflow> 

请参阅

参考

使用 RAW 模式
使用 AUTO 模式
使用 FOR XML 构造 XML

概念

使用 PATH 模式

其他资源

SELECT (Transact-SQL)

帮助和信息

获取 SQL Server 2005 帮助