XML 构造 (XQuery)

适用范围:SQL Server

在 XQuery 中,可以使用 直接 构造函数和 计算 构造函数在查询中构造 XML 结构。

注意

直接构造函数和计算构造函数之间没有区别。

使用直接构造函数

使用直接构造函数时,可以在构造 XML 时指定类似 XML 的语法。 下列示例说明了直接构造函数的 XML 构造。

构造元素

使用 XML 表示法时,可以构造元素。 以下示例使用直接元素构造函数表达式并创建一个 <ProductModel> 元素。 构造的元素有三个子元素。

  • 一个文本节点。

  • 两个元素节点和 <Summary><Features>.

    • 该元素具有一个值为 "Some description"<Summary> 的文本节点子元素。

    • <Features>元素具有三个元素节点子级,<Color><Weight>以及<Warranty>。 其中每个节点都有一个文本节点子节点,并分别具有值Red252 years parts and labor

DECLARE @x AS XML;

SET @x = '';

SELECT @x.query('<ProductModel ProductModelID="111">;
This is product model catalog description.
<Summary>Some description</Summary>
<Features>
  <Color>Red</Color>
  <Weight>25</Weight>
  <Warranty>2 years parts and labor</Warranty>
</Features></ProductModel>');

下面是生成的 XML:

<ProductModel ProductModelID="111">
  This is product model catalog description.
  <Summary>Some description</Summary>
  <Features>
    <Color>Red</Color>
    <Weight>25</Weight>
    <Warranty>2 years parts and labor</Warranty>
  </Features>
</ProductModel>

尽管从常量表达式构造元素(如本例所示)很有用,但是此 XQuery 语言功能的真正作用是构造能够从数据库动态提取数据的 XML。 可以使用大括号指定查询表达式。 在生成的 XML 中,表达式将由其值取代。 例如,以下查询构造一个具有一个 <NewRoot> 子元素的元素(<e>)。 通过在大括号(“{ ... }”)内指定路径表达式来计算元素 <e> 的值。

DECLARE @x AS XML;

SET @x = '<root>5</root>';

SELECT @x.query('<NewRoot><e> { /root } </e></NewRoot>');

大括号充当上下文切换标记,并将查询从 XML 构造切换到查询计算。 在本例中,将对大括号中的 XQuery 路径表达式 /root 进行计算,并用结果替换该表达式。

结果如下:

<NewRoot>
  <e>
    <root>5</root>
  </e>
</NewRoot>

下面的查询与上一个查询类似。 但是,大括号中的表达式指定data()用于检索元素的<root>原子值并将其分配给构造的元素的函数。 <e>

DECLARE @x AS XML;
SET @x = '<root>5</root>';

DECLARE @y AS XML;
SET @y = (SELECT @x.query('
                           <NewRoot>
                             <e> { data(/root) } </e>
                           </NewRoot>'));

SELECT @y;

结果如下:

<NewRoot>
  <e>5</e>
</NewRoot>

若要将大括号用作文本的一部分,而不是上下文切换标记,则可以将其转义为“}}”或“{{”,如下面的示例所示:

DECLARE @x AS XML;
SET @x = '<root>5</root>';

DECLARE @y AS XML;
SET @y = (SELECT @x.query('
<NewRoot> Hello, I can use {{ and  }} as part of my text</NewRoot>'));

SELECT @y;

结果如下:

<NewRoot> Hello, I can use { and  } as part of my text</NewRoot>

下面的查询是使用直接元素构造函数来构造元素的另一个示例。 此外,通过在大括号中执行表达式来获取元素的值 <FirstLocation> 。 该查询表达式返回 Production.ProductModel 表的 Instructions 列中的第一个生产车间的生产步骤。

SELECT Instructions.query('
    declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
        <FirstLocation>
           { /AWMI:root/AWMI:Location[1]/AWMI:step }
        </FirstLocation>
') AS Result
FROM Production.ProductModel
WHERE ProductModelID = 7;

结果如下:

<FirstLocation>
  <AWMI:step xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions">
      Insert <AWMI:material>aluminum sheet MS-2341</AWMI:material> into the <AWMI:tool>T-85A framing tool</AWMI:tool>.
  </AWMI:step>
  <AWMI:step xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions">
      Attach <AWMI:tool>Trim Jig TJ-26</AWMI:tool> to the upper and lower right corners of the aluminum sheet.
  </AWMI:step>
   ...
</FirstLocation>

XML 构造中的元素内容

下面的示例说明了使用直接元素构造函数构造元素内容时表达式的行为。 在下面的示例中,直接元素构造函数指定了一个表达式。 对于此表达式,生成的 XML 中创建了一个文本节点。

DECLARE @x AS XML;
SET @x = '
<root>
  <step>This is step 1</step>
  <step>This is step 2</step>
  <step>This is step 3</step>
</root>';

SELECT @x.query('
<result>
 { for $i in /root[1]/step
    return string($i)
 }
</result>');

从表达式计算得到的原子值序列被添加到该文本节点,并在两个相邻的原子值之间添加一个空格,如结果所示。 构造的元素有一个子元素。 这是一个包含结果中所显示值的文本节点。

<result>This is step 1 This is step 2 This is step 3</result>

如果指定三个单独的表达式(而不是一个表达式)生成三个文本节点,则在生成的 XML 中,相邻的文本节点将通过串联合并为一个文本节点。

DECLARE @x AS XML;
SET @x = '
<root>
  <step>This is step 1</step>
  <step>This is step 2</step>
  <step>This is step 3</step>
</root>';

SELECT @x.query('
<result>
 { string(/root[1]/step[1]) }
 { string(/root[1]/step[2]) }
 { string(/root[1]/step[3]) }
</result>');

构造的元素节点有一个子元素节点。 这是一个包含结果中所显示值的文本节点。

<result>This is step 1This is step 2This is step 3</result>

构造属性

使用直接元素构造函数构造元素时,还可以使用类似于 XML 的语法指定元素的属性,如以下示例所示:

DECLARE @x AS XML;
SET @x = '';

SELECT @x.query('<ProductModel ProductModelID="111">;
This is product model catalog description.
<Summary>Some description</Summary>
</ProductModel>');

下面是生成的 XML:

<ProductModel ProductModelID="111">
  This is product model catalog description.
  <Summary>Some description</Summary>
</ProductModel>

构造的元素 <ProductModel> 具有 ProductModelID 属性和以下子节点:

  • 文本节点 This is product model catalog description.

  • 元素节点。 <Summary> 此节点有一个子文本节点 Some description

构造属性时,可以使用大括号中的表达式指定其值。 在本例中,表达式的结果返回为属性值。

在以下示例中, data() 该函数不严格是必需的。 由于将表达式值分配给属性, data() 因此隐式应用以检索指定表达式的类型化值。

DECLARE @x AS XML;
SET @x = '<root>5</root>';

DECLARE @y AS XML;
SET @y = (SELECT @x.query('<NewRoot attr="{ data(/root) }" ></NewRoot>'));

SELECT @y;

结果如下:

<NewRoot attr="5" />

下面是另一个示例,其中为 LocationID 和 SetupHrs 属性构造指定了表达式。 这些表达式将根据 Instruction 列中的 XML 进行计算。 表达式的类型化值将分配给这些属性。

SELECT Instructions.query('
    declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
        <FirstLocation
         LocationID="{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"
         SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >
           { /AWMI:root/AWMI:Location[1]/AWMI:step }
        </FirstLocation>
') AS Result
FROM Production.ProductModel
WHERE ProductModelID = 7;

下面是部分结果:

<FirstLocation LocationID="10" SetupHours="0.5" >
  <AWMI:step ...
  </AWMI:step>
  ...
</FirstLocation>

实现限制

限制如下:

  • 不支持多个或混合(字符串和 XQuery 表达式)属性表达式。 例如,下面的查询中,您构造的 XML,其中 Item 是一个常量,值 5 是通过计算查询表达式获得的:

    <a attr="Item 5" />
    

    以下查询返回错误,因为你正在将常量字符串与表达式({/x})混合,因此不受支持:

    DECLARE @x AS XML;
    SET @x = '<x>5</x>';
    
    SELECT @x.query('<a attr="Item {/x}"/>');
    

    在这种情况下,您有以下几种选择:

    • 通过串联两个原子值,组成属性值。 这些原子值被序列化为属性值,并且两个原子值之间有一个空格:

      SELECT @x.query('<a attr="{''Item'', data(/x)}"/>');
      

      结果如下:

      <a attr="Item 5" />
      
    • 使用 concat 函数将两个字符串参数连接到生成的属性值:

      SELECT @x.query('<a attr="{concat(''Item'', /x[1])}"/>');
      

      在这种情况下,两个字符串值之间没有添加空格。 如果希望两个值之间有空格,必须显式提供空格。

      结果如下:

      <a attr="Item5" />
      
  • 不支持将多个表达式用作属性值。 例如,下面的查询将返回错误:

    DECLARE @x AS XML;
    SET @x = '<x>5</x>';
    
    SELECT @x.query('<a attr="{/x}{/x}"/>');
    
  • 不支持异类序列。 任何将异类序列分配为属性值的尝试都返回错误,如以下示例所示。 在此示例中,将异类序列(字符串“Item”和元素 <x>)指定为属性值:

    DECLARE @x AS XML;
    SET @x = '<x>5</x>';
    
    SELECT @x.query('<a attr="{''Item'', /x }" />');
    

    如果应用 data() 函数,查询会正常工作,因为它检索表达式的原子值, /x该表达式与字符串串联。 下面是原子值的序列:

    SELECT @x.query('<a attr="{''Item'', data(/x)}"/>');
    

    结果如下:

    <a attr="Item 5" />
    
  • 属性节点顺序是在序列化期间而非静态类型检查期间执行的。 例如,以下查询的失败是因它尝试在非属性节点后添加一个属性造成的。

    SELECT CONVERT (XML, '').query('
        element x { attribute att { "pass" }, element y { "Element text" }, attribute att2 { "fail" } }
        ');
    GO
    

    上一个查询返回以下错误:

    XML well-formedness check: Attribute cannot appear outside of element declaration. Rewrite your XQuery so it returns well-formed XML.
    

添加命名空间

使用直接构造函数构造 XML 时,可以使用命名空间前缀限定构造的元素和属性名称。 可以通过下列方法将前缀绑定到命名空间:

  • 使用命名空间声明属性。
  • 通过使用子 WITH XMLNAMESPACES 句。
  • 在 XQuery prolog 中。

使用命名空间声明属性添加命名空间

以下示例使用元素 <a> 构造中的命名空间声明属性来声明默认命名空间。 子元素 <b> 的构造可撤消父元素中声明的默认命名空间的声明。

DECLARE @x AS XML;
SET @x = '<x>5</x>';

SELECT @x.query('
  <a xmlns="a">
    <b xmlns=""/>
  </a>');

结果如下:

<a xmlns="a">
  <b xmlns="" />
</a>

可以将前缀指定给命名空间。 前缀在元素 <a>构造中指定。

DECLARE @x AS XML;
SET @x = '<x>5</x>';

SELECT @x.query('
  <x:a xmlns:x="a">
    <b/>
  </x:a>');

结果如下:

<x:a xmlns:x="a">
  <b />
</x:a>

可以在 XML 构造中取消声明默认命名空间,但不能取消声明命名空间前缀。 以下查询返回错误,因为不能取消声明元素 <b>构造中指定的前缀。

DECLARE @x AS XML;
SET @x = '<x>5</x>';

SELECT @x.query('
  <x:a xmlns:x="a">
    <b xmlns:x=""/>
  </x:a>');

新构造的命名空间可在查询中使用。 例如,以下查询在构造元素时声明命名空间, <FirstLocation>并在 LocationID 和 SetupHrs 属性值的表达式中指定前缀。

SELECT Instructions.query('
        <FirstLocation xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions"
         LocationID="{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"
         SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >
           { /AWMI:root/AWMI:Location[1]/AWMI:step }
        </FirstLocation>
') AS Result
FROM Production.ProductModel
WHERE ProductModelID = 7;

以这种方式创建新的命名空间前缀会替代此前缀的任何预先存在的命名空间声明。 例如,查询 prolog 中的命名空间声明 AWMI="https://someURI"由元素中的 <FirstLocation> 命名空间声明重写。

SELECT Instructions.query('
declare namespace AWMI="https://someURI";
        <FirstLocation xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions"
         LocationID="{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"
         SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >
           { /AWMI:root/AWMI:Location[1]/AWMI:step }
        </FirstLocation>
') AS Result
FROM Production.ProductModel
WHERE ProductModelID = 7;

使用 prolog 添加命名空间

下面的示例说明了如何将命名空间添加到构造的 XML 中。 查询 prolog 中已声明默认命名空间。

DECLARE @x AS XML;
SET @x = '<x>5</x>';

SELECT @x.query('
           declare default element namespace "a";
            <a><b xmlns=""/></a>');

在元素 <b>构造中,命名空间声明属性指定为空字符串作为其值。 这将取消声明父级中所声明的默认命名空间。

结果如下:

<a xmlns="a">
  <b xmlns="" />
</a>

XML 构造和空白处理

XML 构造中的元素内容可以包含空格字符。 这些字符以下列方式进行处理:

  • 命名空间 URI 中的空格字符被视为 XSD 类型 anyURI。 具体而言,这是如何处理它们:

    • 修整开头和结尾处的所有空格字符。
    • 将内部空格字符值折叠为一个空格。
  • 将属性内容中的换行符替换为空格。 所有其他空格字符保持不变。

  • 保留元素中的空格。

下面的示例说明了如何处理 XML 构造中的空格:

-- line feed is replaced by space.
DECLARE @x AS XML;
SET @x = '';

SELECT @x.query('

declare namespace myNS="   https://
 abc/
xyz

";
<test attr="    my
test   attr
value    " >

<a>

This     is  a

test

</a>
</test>
') AS XML_Result;

结果如下:

-- result
<test attr="<test attr="    my test   attr  value    "><a>

This     is  a

test

</a></test>
"><a>

This     is  a

test

</a></test>

其他直接 XML 构造函数

用于处理指令和 XML 注释的构造函数使用与相应的 XML 构造语法相同的语法。 还支持文本节点的计算构造函数,但主要用于 XML DML 构造文本节点。

注意

有关使用显式文本节点构造函数的示例,请参阅插入中的特定示例(XML DML)。

在下面的查询中,构造的 XML 包括一个元素、两个属性、一个注释和一个处理指令。 在构造序列之前 <FirstLocation>使用逗号。

SELECT Instructions.query('
  declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
   <?myProcessingInstr abc="value" ?>,
   <FirstLocation
        WorkCtrID = "{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"
        SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >
       <!-- some comment -->
       <?myPI some processing instructions ?>
       { (/AWMI:root/AWMI:Location[1]/AWMI:step) }
    </FirstLocation>
') AS Result
FROM Production.ProductModel
WHERE ProductModelID = 7;

下面是部分结果:

<?myProcessingInstr abc="value" ?>
<FirstLocation WorkCtrID="10" SetupHrs="0.5">
  <!-- some comment -->
  <?myPI some processing instructions ?>
  <AWMI:step xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions">I
  nsert <AWMI:material>aluminum sheet MS-2341</AWMI:material> into the <AWMI:tool>T-85A framing tool</AWMI:tool>.
  </AWMI:step>
    ...
</FirstLocation>

使用计算构造函数

本例中,指定了关键字来标识要构造的节点的类型。 仅支持下列关键字:

  • 元素
  • 属性
  • 文本消息

对于元素节点和属性节点,这些关键字后面跟有节点名称和生成该节点内容的表达式(括在大括号内)。 在以下示例中,你正在构造此 XML:

<root>
  <ProductModel PID="5">Some text <summary>Some Summary</summary></ProductModel>
</root>

下面是使用计算构造函数的查询生成 XML:

DECLARE @x AS XML;
SET @x = '';

SELECT @x.query('element root
               {
                  element ProductModel
     {
attribute PID { 5 },
text{"Some text "},
    element summary { "Some Summary" }
 }
               } ');

生成节点内容的表达式可以指定一个查询表达式。

DECLARE @x AS XML;
SET @x = '<a attr="5"><b>some summary</b></a>';

SELECT @x.query('element root
               {
                  element ProductModel
     {
attribute PID { /a/@attr },
text{"Some text "},
    element summary { /a/b }
 }
               } ');

通过 XQuery 规范中定义的计算元素和属性构造函数,可以计算节点名称。 在 SQL Server 中使用直接构造函数时,必须将节点名称(如元素和属性)指定为常量文本。 因此,对于元素和属性,直接构造函数和计算构造函数没有区别。

在下面的示例中,从 ProductModel 表中 xml 数据类型的“指令”列中 存储的 XML 制造指令获取构造节点的内容。

SELECT Instructions.query('
  declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
   element FirstLocation
     {
        attribute LocationID { (/AWMI:root/AWMI:Location[1]/@LocationID)[1] },
        element   AllTheSteps { /AWMI:root/AWMI:Location[1]/AWMI:step }
     }
') AS Result
FROM Production.ProductModel
WHERE ProductModelID = 7;

下面是部分结果:

<FirstLocation LocationID="10">
  <AllTheSteps>
    <AWMI:step> ... </AWMI:step>
    <AWMI:step> ... </AWMI:step>
    ...
  </AllTheSteps>
</FirstLocation>

其他实现限制

计算属性构造函数不能用于声明新命名空间。 此外,SQL Server 不支持以下计算构造函数:

  • 计算文档节点构造函数
  • 计算处理指令构造函数
  • 计算注释构造函数