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

XQuery 基础知识

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (9投票s)

2013年6月19日

CPOL

4分钟阅读

viewsIcon

43562

XQuery 基础。

引言

XML 在传输和持久化数据方面有很大的潜力,可以利用 XML 的能力进行开发。通常,XML 用于数据同步,使数据同步更加冗长。在 SQL Server 2005 中,数据库引擎引入了 XQuery 支持,以便查询结构化 XML 数据。XQuery 通常与 XPath 表达式配合使用,并额外支持节点迭代、排序和构建各种所需的 XML 实例。

XQuery 遵循 XPath 1.0 表达式。XPath 1.0 包含从结构化 XML 实例中检索特定节点信息的通用语法。

XSLT 是另一种用于 XML 转换的语言。XSLT 也遵循 XPath 1.0 规则来转换 XML 数据。通常,XSLT 用于将 XML 转换为 HTML。XSLT 适用于顺序节点,但不适用于值序列。在 XSLT 中应用 JOIN 和函数很困难,因此在这方面 XQuery 更有优势,因为您可以在 XQuery 中轻松地使用函数和 JOIN。

在 SQL Server 中,使用 Open XML 在拆分大型 XML 数据文件方面效果很好,但对于小型 XML 数据文件,它的内存占用比 XQuery 更大。我们可以根据需要对 XML 数据应用 JOIN 和函数。它提供了更好的代码可管理性。

本文旨在通过解释一些场景来展示 XQuery 的工作原理。

与 Open XML 相比,使用 XQuery 有许多优点。

  • 与相似的 SQL 语法相比,XML 语法更短。
  • 灵活的查询结构,可获得树状视图和表格结果。
  • 内置 XQuery 函数。

概述:本文仅适合希望轻松入门 XQuery 的初学者。本文演示了如何在 SQL Server 中使用 XQuery 查询和操作 XML 数据。我不会深入探讨,因为它是一个太大的技术话题,在这篇文章中,我只想通过提供一些简单的演示来展示初学者如何快速入门 XQuery。

在这里,我们将通过演示一些场景来理解一些 XQuery 方法。

CREATE TABLE #TempXML(
      [DocId] [int] IDENTITY(1,1) NOT NULL,
      [Doc] [xml] NULL

)
CREATE TABLE #TempRegion(
      [RegionId] [int] IDENTITY(1,1) NOT NULL,
      [Region] [varchar](100) NULL

)

首先,我们在 SQL 临时表中创建两个 XML 实例,一个没有命名空间,另一个使用默认命名空间,以了解 XQuery 如何处理这些类型。

Insert Into #TempXML Values ('<Persons>

<Person>
    <Name>Demo1</Name>
    <Salary>10000</Salary>
   
<Designation>Developer</Designation>
    <RegionId>1</RegionId>
  </Person>
  <Person>
    <Name>Demo2</Name>
    <Salary>12000</Salary>
    <Designation>Sr.
Developer</Designation>
    <RegionId>2</RegionId>
  </Person>
  <Person>
    <Name>Demo3</Name>
    <Salary>13000</Salary>
    <Designation>Sr.
Developer</Designation>
    <RegionId>3</RegionId>
  </Person>
  <Person>
    <Name>Demo4</Name>
    <Salary>14000</Salary>
   
<Designation>Developer</Designation>
    <RegionId>5</RegionId>
  </Person>
</Persons>')
Insert Into #TempXML Values ('<Persons
xmlns="http://schemas.test.App">
  <Person>
    <Name>Demo1</Name>
    <Salary>10000</Salary>
   
<Designation>Developer</Designation>
    <RegionId>1</RegionId>
  </Person>
  <Person>
    <Name>Demo2</Name>
    <Salary>12000</Salary>
    <Designation>Sr.
Developer</Designation>
    <RegionId>2</RegionId>
  </Person>
  <Person>
    <Name>Demo3</Name>
    <Salary>13000</Salary>
    <Designation>Sr.
Developer</Designation>
    <RegionId>3</RegionId>
  </Person>
  <Person>
    <Name>Demo4</Name>
    <Salary>14000</Salary>
   
<Designation>Developer</Designation>
    <RegionId>5</RegionId>
  </Person>

</Persons>')
Insert
#TempRegion Values ('East')
Insert
#TempRegion Values ('West')
Insert
#TempRegion Values ('North')

Insert #TempRegion Values ('South')

query() 方法:query() 方法用于通过评估 XPath 表达式来处理提供的 XML 节点和碎片 XML 文档。Query 方法也接受 "FLOWR" 表达式来检索相对的、未类型化的 XML 实例结果。

查询薪资大于 12000 的 Person(s)(不含命名空间)

Select 
Doc.query('/Persons/Person[Salary>12000]')
From #TempXML

WHERE DocId=1

查询在 XML 实例中薪资大于 12000 的 Person(s)(其中指定了默认命名空间)。

这里有两种处理默认命名空间的方法。

;WITH
XMLNAMESPACES( default 'http://schemas.test.App' )
Select 
Doc.query('/Persons/Person[Salary>12000]')
From #TempXML

WHERE DocId=2
Select 
Doc.query('declare default element
namespace "http://schemas.test.App"; 
           /Persons/Person[Salary>12000]')
From #TempXML

WHERE DocId=2

如果 XML 命名空间不是默认实例,则声明为。

Xmlns:PER=http://schemas.test.App

通过声明 XQuery Prolog。

declare namespace PER="http://schemas.test.App";
  • XQuery Prolog 包括一个命名空间前缀 (PER) 声明,
    (Namespace PER="http://schemas.test.App";
  • declare namespace 关键字定义了一个命名空间前缀,该前缀在查询正文中稍后使用。
  • /PER:Persons/PER:Person[Name="Demo"] 是查询正文。

使用 FLOWR 表达式。

FLWOR 发音为“flower”。FLOWR 代表 FOR、LET、ORDER BY、WHERE 和 RETURN。其中大部分是可选的,只有 return 子句是强制的,至少需要一个 FOR 或 LET 子句。FLWOR 表达式也用于查询或迭代 XML 实例。以下是一些示例,以了解它在 SQL 中的工作原理。

查询薪资大于等于 12000 的 Person(s)。

Select 
Doc.query('for $A in
/Persons/Person/Salary[.>="12000"]/..
              return 
              $A ')
From #TempXML

WHERE DocId=1

查询 Person(s) (薪资):

salary >=12000 (Use of WHERE and ORDER BY)
Select 
Doc.query('for $A in /Persons/Person
             where $A/Salary>=12000
             order by $A/Name[1]
descending            
             return $A')
From #TempXML

WHERE DocId=1

查询薪资小于等于 12000 的 Person(s) 并增加 1200 薪资(使用 LET)。

Select
 Doc.query('for $T in /Persons/Person
            let $L := 1200
            where $T/Salary[1]<=12000
            return
               <Person
Name="{data($T/Name)}"
RestructuredSalary="{data($L)+($T/Salary/text())[1]}"/>')
FROM #TempXML

Where DocId=1

value() 方法:此方法用于从 XML 实例中检索标量文本值。相关的 XQuery 表达式,它识别单节点和文本值类型,会返回节点文本值。返回的节点值会转换为提供的 SQL 类型。

;WITH
XMLNAMESPACES( default 'http://schemas.test.App' )
SELECT 
Name=x.value('(Name/text())[1]','varchar(100)'),
Salary=x.value('(Salary/text())[1]','varchar(100)'),
Designation=x.value('(Designation/text())[1]','varchar(100)')
From #TempXML 
     CROSS APPLY Doc.nodes('/Persons/Person') as e(x)

Where DocId=2

在下面的场景中,我们通过在 XML 实例和用户表 #TempRegion 之间应用 JOIN 来提取当前分配给 person(s) 的区域。

SELECT tR.Region
       FROM  #TempXML tX Left JOIN
             #TempRegion tR 
             on
tX.Doc.exist('/Persons/Person/RegionId[.=sql:column("tR.RegionId")]') = 1

       Where DocId=1

如果您想按区域提取特定 person(s) 节点,则可以如下操作。

SELECT tX.Doc.query('/Persons/Person[RegionId=sql:column("tR.RegionId")]'),tR.Region
       FROM  #TempXML tX Left JOIN
             #TempRegion tR 
             on
tX.Doc.exist('/Persons/Person/RegionId[.=sql:column("tR.RegionId")]') = 1

       Where DocId=1

在 XQuery 中使用函数。

检索薪资大于 12000 的 Person(s) 的计数(使用 count)。

Select 
PersonCount=Doc.value('count(/Persons/Person[Salary>12000])','int')
From #TempXML
WHERE DocId=1

检索薪资大于 12000 的 Person(s) 的平均薪资(使用 avg)。

Select 
PersonCount=Doc.value('avg(/Persons/Person[Salary>12000]/Salary)','int')
From #TempXML
WHERE DocId=1

Xquery 具有以下内置函数。

字符串函数

  • concat:连接字符串实例,类似于 SQL 中的用法。

  • contains:搜索指定的字符串。

  • substring:从其他字符串中检索字符串的一部分。
  • string-length:确定指定字符串的长度。

聚合函数:min()max()sum()

上下文函数:last()position()

操作 XML 实例中的数据。

要修改 XML 节点及其值,XQuery XML 查询语言支持一个附加模块,称为 XML 数据修改语言 (DML),通过在 XQuery modify() 方法中使用 **insert**、**replace value of** 和 **delete** 来实现。

向 Name = Demo1 的 Person 节点插入 XML 实例。

UPDATE #TempXML
SET
    Doc.modify('replace value of 
                
(/Persons/Person[DeploymentStatus[@IsDeployed="True"]]/Salary/text())[1]

                   with ("15000")')

WHERE DocId=1

将 Name 为 Deployment Status 的 Person 的 Salary 节点文本替换为 15000,将 Deployment Status 值设置为 True。

UPDATE #TempXML
SET
    Doc.modify('replace value of 
                
(/Persons/Person[DeploymentStatus[@IsDeployed="True"]]/Salary/text())[1]

                   with ("15000")')

WHERE DocId=1

从 Name 为 Demo1 的 Person 中删除 DeploymentStatus 节点。

UPDATE #TempXML
SET
    Doc.modify('delete
(/Persons/Person[Name="Demo1"]/DeploymentStatus)') 

WHERE DocId=1

提示:Xquery 同时向主表和引用子表插入数据。

有很多方法可以实现这一点。这是使用 XQuery 和 Merge 语句将数据插入主表和引用子表的一个提示。

Create table #Master
(
 Id int Identity(1,1),
 MasterName Varchar(100)
)

Create Table #Intermediate
(MasterId int,
 ChildData XML)
 
 Create Table #Child
 (
 ChildId int identity(1,1),
 MasterId int,
 ChildName Varchar(100)
 )

Declare @XML XML='<Record>
                     <MasterRecord>
                       
<Master>Master1</Master>
                        <ChildRecord>
                            <Child>Child11</Child>
                           
<Child>Child12</Child>
                        </ChildRecord>
                     </MasterRecord>
                     <MasterRecord>
                       
<Master>Master2</Master>
                        <ChildRecord>
                           
<Child>Child21</Child>
                           
<Child>Child22</Child>
                        </ChildRecord>
                     </MasterRecord>
                  </Record>'
                  
  
        
MERGE #Master
_MTR
USING (Select x.value('Master[1]','varchar(255)') AS masterName ,
              x.query('ChildRecord/Child') AS ChildData          
              From
@XML.nodes('/Record/MasterRecord')
e(x)) AS _XML
ON 1=0
WHEN NOT MATCHED THEN
INSERT (MasterName)
VALUES(_XML.MasterName)
OUTPUT INSERTED.Id, _XML.ChildData
INTO
#Intermediate(MasterId,ChildData);


Insert Into #Child(MasterId,ChildName) 
Select  mas.Id, _data.value('(text())[1]', 'varchar(100)') as ChildName
from
#Intermediate intr Inner Join #Master Mas
     On intr.MasterId=Mas.Id
CROSS APPLY ChildData.nodes('/Child') AS _chd(_data)

Select * from #Master
Select * from #Intermediate

select * from #Child
© . All rights reserved.