Click here to view and discuss this page in DocCommentXchange. In the future, you will be sent there automatically.

SQL Anywhere 11.0.1 (中文) » SQL Anywhere 服务器 - SQL 的用法 » 数据库中的 XML » 在数据库中使用 XML » 以 XML 格式获取查询结果

 

使用 FOR XML EXPLICIT

使用 FOR XML EXPLICIT,您可以控制查询所返回的 XML 文档的结构。查询必须用特定的方法编写,以在查询结果中指定有关您需要的嵌套的信息。使用 FOR XML EXPLICIT 支持的可选指令,您可以配置各列的处理方式。例如,您可以对以下情况加以控制:让列显示为元素内容还是显示为属性内容,或者,列是否只用于给结果排序,而不会出现在生成的 XML 中。

有关如何使用 FOR XML EXPLICIT 编写查询的示例,请参见编写 EXPLICIT 模式的查询

参数

在 EXPLICIT 模式中,SELECT 语句中的前两列必须分别命名为 TagParent。Tag 和 Parent 是元数据列,它们的值用来确定由查询返回的 XML 文档中各元素之间的父子关系(或嵌套)。

  • Tag 列   这是在选择列表中指定的第一列。Tag 列存储当前元素的标记值。标记号可以使用的值是 1 到 255。

  • Parent 列   此列存储当前元素的父项的标记号。如果此列中的值为 NULL,则行就会被放置在 XML 层次的顶层。

例如,假定有这样一个查询,它在没有指定 FOR XML EXPLICIT 时会返回以下结果集。(将会在下一节向查询添加数据列中介绍 GivenName!1 和 ID!2 数据列的用途。)

Tag Parent GivenName!1 ID!2
1 NULL 'Beth' NULL
2 NULL NULL '102'

在本示例中,Tag 列中的值是结果集中各元素的标记号。两行的 Parent 列都包含 NULL 值。这意味着两个元素都会在层次的顶层生成,当查询包括 FOR XML EXPLICIT 子句时,将给出以下结果:

<GivenName>Beth</GivenName>
<ID>102</ID>

但是,如果第二行的 Parent 列的值是 1,结果将如下所示:

<GivenName>Beth
   <ID>102</ID>
</GivenName>

有关如何使用 FOR XML EXPLICIT 编写查询的示例,请参见编写 EXPLICIT 模式的查询

向查询添加数据列

除 Tag 列和 Parent 列之外,查询还必须包含一个或多个数据列。这些数据列的名称控制着在标记过程中解释列的方式。每个列名都拆分为由惊叹号 (!) 分隔的字段。可以为数据列指定以下字段:

ElementName!TagNumber!AttributeName!Directive

ElementName   元素的名称。对于给定的行,会从标记号匹配的第一列的 ElementName 字段中提取为该行生成的元素的名称。如果有多个 TagNumber 相同的列,对于后面的 TagNumber相同的列,会忽略 ElementName。在上面的示例中,第一行会生成一个名为 <GivenName> 的元素。

TagNumber   元素的标记号。对于有给定标记值的行,所有在其 TagNumber 字段中具有相同值的列都会向与该行相对应的元素提供内容。

AttributeName   指定列值是 ElementName 元素的属性。例如,如果数据列具有名称 productID!1!Color,那么 Color 就会显示为 <productID> 元素的一个属性。

Directive   使用此可选字段,您可以进一步控制 XML 文档的格式。您可以为 Directive 指定以下任意一个值:

  • hide   指明在生成结果时忽略此列。这条指令可以用于加入只用来给表排序的列。属性名会被忽略,且不出现在结果中。

    有关使用 hide 指令的示例,请参见使用 hide 指令

  • element   指明列值作为名称为 AttributeName 的嵌套元素插入,而不是作为属性。

    有关使用 element 指令的示例,请参见使用 element 指令

  • xml   指明列值插入时不进行引用。如果指定了 AttributeName,则值作为使用该名称的元素插入。否则,它在插入时没有包装元素。如果不使用此指令,标记字符就会被转义,除非列是 XML 类型。例如,值 <a/> 将以 &lt;a/&gt; 形式插入。

    有关使用 xml 指令的示例,请参见使用 xml 指令

  • cdata   指明列值将作为 CDATA 区段插入。AttributeName 会被忽略。

    有关使用 cdata 指令的示例,请参见使用 cdata 指令

用法

在执行包含 FOR XML EXPLICIT 的查询时,BINARY、LONG BINARY、IMAGE 和 VARBINARY 列中的数据会自动以 base64 编码格式返回。缺省情况下,结果集中的任何 NULL 值都会被忽略。通过更改 for_xml_null_treatment 选项的设置,您可以更改此行为。

有关 for_xml_null_treatment 选项的详细信息,请参见for_xml_null_treatment 选项 [数据库]FOR XML 和 NULL 值

编写 EXPLICIT 模式的查询

假定您要使用 FOR XML EXPLICIT 编写一个生成以下 XML 文档的查询:

<employee EmployeeID='129'>
   <customer CustomerID='107' Region='Eastern'/>
   <customer CustomerID='119' Region='Western'/>
   <customer CustomerID='131' Region='Eastern'/>
</employee>
<employee EmployeeID='195'>
   <customer CustomerID='109' Region='Eastern'/>
   <customer CustomerID='121' Region='Central'/>
</employee>

实现方法是:编写一条按指定的准确顺序返回以下结果集的 SELECT 语句,然后将 FOR XML EXPLICIT 附加到该查询。

Tag Parent employee!1!EmployeeID customer!2!CustomerID customer!2!Region
1 NULL 129 NULL NULL
2 1 129 107 Eastern
2 1 129 119 Western
2 1 129 131 Central
1 NULL 195 NULL NULL
2 1 195 109 Eastern
2 1 195 121 Central

在编写查询时,对于某指定行,只有部分列会成为所生成的 XML 文档的组成部分。仅当 TagNumber 字段(列名中的第二个字段)中的值与 Tag 列中的值匹配时,XML 文档中才会加入一列。

在该示例中,第三列用于在其 Tag 列中有值 1 的两个行。在第四和第五列中,值用于在其 Tag 列中有值 2 的行。从列名中的第一个字段中提取元素名。在本例中,会创建 <employee> 元素和 <customer> 元素。

属性名来自列名中的第三个字段,所以,EmployeeID 属性是为 <employee> 元素创建的,而 CustomerID 属性和 Region 属性是为 <customer> 元素生成的。

以下步骤介绍了如何使用 SQL Anywhere 示例数据库构建一个 FOR XML EXPLICIT 查询,让该查询生成一个与本节开始时出现的 XML 文档类似的 XML 文档。

♦  编写 FOR XML EXPLICIT 查询
  1. 编写 SELECT 语句以生成顶层元素。

    在本示例中,查询中的第一个 SELECT 语句会生成 <employee> 元素。查询中的前两个值必须是 Tag 和 Parent 列值。<employee> 元素位于层次的顶层,所以会给它分配一个 Tag 值 1 以及一个 Parent 值 NULL。

    注意

    如果您要编写一个使用 UNION 的 EXPLICIT 模式查询,则只有在第一条 SELECT 语句中指定的列名才会被使用。要用作元素名或属性名的列名必须在第一条 SELECT 语句中指定,因为在后续 SELECT 语句中指定的列名会被忽略。

    要为上面的表生成 <employee> 元素,您的第一条 SELECT 语句应如下所示:

    SELECT
          1          AS tag,
          NULL       AS parent,
          EmployeeID AS [employee!1!EmployeeID],
          NULL       AS [customer!2!CustomerID],
          NULL       AS [customer!2!Region]
    FROM Employees;
  2. 编写 SELECT 语句来生成子元素。

    第二个查询会生成 <customer> 元素。由于这是一个 EXPLICIT 模式查询,所以在所有 SELECT 语句中指定的前两个值都必须是 Tag 值和 Parent 值。<customer> 元素会得到标记号 2,而由于它是 <employee> 元素的子项,因此具有 Parent 值 1。第一条 SELECT 语句已经指出 EmployeeID、CustomerID 和 Region 都是属性。

    SELECT
          2,
          1,
          EmployeeID,
          CustomerID,
          Region
    FROM Employees KEY JOIN SalesOrders
  3. 给查询添加 UNION ALL 以便将两条 SELECT 语句合并到一起:

    SELECT
          1          AS tag,
          NULL       AS parent,
          EmployeeID AS [employee!1!EmployeeID],
          NULL       AS [customer!2!CustomerID],
          NULL       AS [customer!2!Region]
    FROM Employees
    UNION ALL 
    SELECT
          2,
          1,
          EmployeeID,
          CustomerID,
          Region
    FROM Employees KEY JOIN SalesOrders
  4. 添加 ORDER BY 子句以指定结果中的行的顺序。行的顺序即在最终生成的文档中使用的顺序。

    SELECT
          1          AS tag,
          NULL       AS parent,
          EmployeeID AS [employee!1!EmployeeID],
          NULL       AS [customer!2!CustomerID],
          NULL       AS [customer!2!Region]
    FROM Employees
    UNION ALL 
    SELECT
          2,
          1,
          EmployeeID,
          CustomerID,
          Region
    FROM Employees KEY JOIN SalesOrders
    ORDER BY 3, 1
    FOR XML EXPLICIT;

有关 EXPLICIT 模式的语法的信息,请参见参数

FOR XML EXPLICIT 示例

以下示例查询会检索有关雇员所下订单的信息。在本示例中,有以下三种类型的元素:<employee>、<order> 和 <department>。<employee> 元素具有 ID 属性和 name 属性,<order> 元素具有 date 属性,<department> 元素具有 name 属性。

SELECT
        1           tag,
        NULL        parent,
        EmployeeID  [employee!1!ID],
        GivenName   [employee!1!name],
        NULL        [order!2!date],
        NULL        [department!3!name]
FROM Employees
UNION ALL 
SELECT
        2,
        1,
        EmployeeID,
        NULL,
        OrderDate,
        NULL
FROM Employees KEY JOIN SalesOrders
UNION ALL 
SELECT
        3,
        1,
        EmployeeID,
        NULL,
        NULL,
        DepartmentName
FROM Employees e JOIN Departments d
   ON e.DepartmentID=d.DepartmentID
ORDER BY 3, 1
FOR XML EXPLICIT;

您将从此查询得到以下结果:

<employee ID="102" name="Fran">
   <department name="R &amp; D"/>
</employee>
<employee ID="105" name="Matthew">
   <department name="R &amp; D"/>
</employee> 
<employee ID="129" name="Philip">
   <order date="2000-07-24"/>
   <order date="2000-07-13"/>
   <order date="2000-06-24"/>
   <order date="2000-06-08"/>
   ...
   <department name="Sales"/>
</employee> 
<employee ID="148" name="Julie">
   <department name="Finance"/>
</employee>
...
使用 element 指令

如果您想生成子元素而不是属性,可以向查询中添加 element 指令,如下所示:

SELECT
        1          tag,
        NULL       parent,
        EmployeeID [employee!1!id!element],
        GivenName  [employee!1!name!element],
        NULL       [order!2!date!element],
        NULL       [department!3!name!element]
FROM Employees 
UNION ALL
SELECT
        2,
        1,
        EmployeeID,
        NULL,
        OrderDate,
        NULL
FROM Employees KEY JOIN SalesOrders
UNION ALL 
SELECT
        3,
        1,
        EmployeeID,
        NULL,
        NULL,
        DepartmentName
FROM Employees e JOIN Departments d
   ON e.DepartmentID=d.DepartmentID
ORDER BY 3, 1
FOR XML EXPLICIT;

您将从此查询得到以下结果:

<employee>
   <id>102</id>
   <name>Fran</name>
   <department>
      <name>R &amp; D</name>
   </department>
</employee> 
<employee>
   <id>105</id>
   <name>Matthew</name>
   <department>
      <name>R &amp; D</name>
   </department>
</employee> 
<employee>
   <id>129</id>
   <name>Philip</name>
   <order>
      <date>2000-07-24</date>
   </order>
   <order>
      <date>2000-07-13</date>
   </order>
   <order>
      <date>2000-06-24</date>
   </order>
   ...
   <department>
      <name>Sales</name>
   </department>
</employee>
...
使用 hide 指令

在以下查询中,雇员 ID 用于为结果排序,但结果中不会出现雇员 ID,因为指定了 hide 指令:

SELECT
        1           tag,
        NULL        parent,
        EmployeeID  [employee!1!id!hide],
        GivenName   [employee!1!name],
        NULL        [order!2!date],
        NULL        [department!3!name]
FROM Employees
UNION ALL 
SELECT
        2,
        1,
        EmployeeID,
        NULL,
        OrderDate,
        NULL
FROM Employees KEY JOIN SalesOrders
UNION ALL 
SELECT
        3,
        1,
        EmployeeID,
        NULL,
        NULL,
        DepartmentName
FROM Employees e JOIN Departments d
   ON e.DepartmentID=d.DepartmentID
ORDER BY 3, 1
FOR XML EXPLICIT;

此查询会返回以下结果:

<employee name="Fran">
   <department name="R &amp; D"/>
</employee>
<employee name="Matthew">
   <department name="R &amp; D"/>
</employee> 
<employee name="Philip">
   <order date="2000-04-21"/>
   <order date="2001-07-23"/>
   <order date="2000-12-30"/>
   <order date="2000-12-20"/>
   ...
   <department name="Sales"/>
</employee> 
<employee name="Julie">
   <department name="Finance"/>
</employee>
...
使用 xml 指令

缺省情况下,当 FOR XML EXPLICIT 查询的结果包含的字符不是有效 XML 字符时,无效字符会被转义(有关信息,请参见对非法的 XML 名称编码),除非该列属于 XML 类型。例如,以下查询会生成包含和符号 (&) 的 XML:

SELECT
        1              AS tag,
        NULL           AS parent,
        ID             AS [customer!1!ID!element],
        CompanyName    AS [customer!1!CompanyName]
FROM Customers
WHERE ID = '115'
FOR XML EXPLICIT;

在此查询所生成的结果中,和符号会被转义,因为该列不属于 XML 类型:

<Customers CompanyName="Sterling &amp; Co.">
   <ID>115</ID>
</Customers>

xml 指令表明列值在插入生成的 XML 中时不进行引用。如果用 xml 指令执行与以上查询相同的查询:

SELECT
        1              AS tag,
        NULL           AS parent,
        ID             AS [customer!1!ID!element],
        CompanyName    AS [customer!1!CompanyName!xml]
FROM Customers
WHERE ID = '115'
FOR XML EXPLICIT;

结果中的和符号将不进行引用:

<customer>
 <ID>115</ID>
 <CompanyName>Sterling & Co.</CompanyName>
</customer>

请注意,此 XML 不是一个结构完好的 XML,因为它含有和符号,而该符号在 XML 中是一个特殊字符。在查询生成 XML 之后,由您负责确保该 XML 结构完好且有效:SQL Anywhere 并不检查所生成的 XML 是否结构完好和有效。

当您指定 xml 指令时,AttributeName 字段会被忽略,并且会生成元素而不是属性。

使用 cdata 指令

以下查询使用 cdata 指令在 CDATA 区段中返回客户名称:

SELECT
        1               AS tag,
        NULL            AS parent,
        ID              AS [product!1!ID],
        Description     AS [product!1!!cdata]
FROM Products
FOR XML EXPLICIT;

此查询生成的结果将在 CDATA 区段中列出对每种产品的说明。CDATA 区段中包含的数据不进行引用:

<product ID="300">
   <![CDATA[Tank Top]]>
</product>
<product ID="301">
   <![CDATA[V-neck]]>
</product> 
<product ID="302">
   <![CDATA[Crew Neck]]>
</product>
<product ID="400">
   <![CDATA[Cotton Cap]]>
</product>
...