XML 构造 (XQuery)

适用范围:SQL Server

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

注意

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

使用直接构造函数

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

构造元素

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

  • 一个文本节点。

  • 两个元素节点: <摘要> 和 <功能>。

    • <Summary> 元素有一个文本节点子节点,其值为“Some description”。

    • Features 元素具有三个元素节点子级、<颜色>、<权重>和<保修>。> < 这些节点中,每个节点都有一个子文本节点,它们的值分别为 Red、25、2 years parts and labor。

declare @x 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 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 xml;  
SET @x='<root>5</root>';  
DECLARE @y xml;  
SET @y = (SELECT @x.query('  
                           <NewRoot>  
                             <e> { data(/root) } </e>  
                           </NewRoot>' ));  
SELECT @y;  

结果如下:

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

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

DECLARE @x xml;  
SET @x='<root>5</root>';  
DECLARE @y 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 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 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 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 xml;  
SET @x='<root>5</root>';  
DECLARE @y 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 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 xml  
    SET @x ='<x>5</x>'  
    SELECT @x.query( '<a attr="{/x}{/x}"/>' )  
    
  • 不支持异类序列。 任何将异类序列指定为属性值的尝试都将返回一个错误,如下面的示例所示。 在此示例中,将异类序列(字符串“Item”和元素 <x>)指定为属性值:

    DECLARE @x 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 xml  
set @x ='<x>5</x>'  
select @x.query( '  
  <a xmlns="a">  
    <b xmlns=""/>  
  </a>' )   

结果如下:

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

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

declare @x 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 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 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 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>  
  

使用计算构造函数

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

  • element

  • attribute

  • text

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

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

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

declare @x xml  
set @x=''  
select @x.query('element root   
               {   
                  element ProductModel  
     {  
attribute PID { 5 },  
text{"Some text "},  
    element summary { "Some Summary" }  
 }  
               } ')  
  

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

declare @x 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 不支持以下计算构造函数:

  • 计算文档节点构造函数

  • 计算处理指令构造函数

  • 计算注释构造函数

另请参阅

XQuery 表达式