在传递数据时,XML和JSON是最常用的数据格式,SQL Server从很早的版本就开始支持XML格式,而对于JSON格式,SQL Server从2016版本开始支持。大多数数据库系统并没有升级到SQL Server 2016版本,因此在传递格式化的数据时,通常还是使用XML格式。对我而言,查询和解析XML格式的数据需要掌握的知识点较多,MSDN上关于XML的文档,又试图把XML的各个方面都讲解地清清楚楚,以至于内容冗杂,使学习过程变得困难。我十分不喜欢学习这些不常用的数据结构,再说,在平时的数据库开发中,用到XML的地方也很少,可是,一旦在应用程序中用到XML,就只有头疼的份了,既然避不开XML,那就用最简单的方法学习它,了解它,使用它,以备不时之需。写这篇文章,就是以最简单的方式,分享XML最常用的使用方法。

一,XML数据格式的简单介绍

1,最简单的XML格式

XML数据最简单的格式是:

  • 开始标签:<tag>
  • 标签的属性,属性值用双引号:<tag id="1" name="azure">,在单个节点中,属性名不能重复,属性之间使用空格分隔,在开始标签中,才能设置属性;
  • 结束标签:</tag>,结束标签不能有属性;
  • 子节点:在开始标签和结束标签,可以包含节点,叫做子节点;
  • 节点值:在开始标签和结束标签的标量值,叫做节点值;

2,使用字符串对XML数据赋值

数据类型XML用于存储XML格式化的文本数据,在本例中,声明一个XML类型的变量 @xml,并赋值,后文示例都使用该变量用于数据查询。

declare @xml xml
set @xml='
<Expression ID="1" TaxonomyID="1">
<SubExpression ID="1" OperandType="Tag" Operator="and">
<Oprand ID="268819" Name="abuse" />
<Oprand ID="277029" Name="mongohq" />
<Oprand ID="516813" Name="access" />
</SubExpression>
<SubExpression ID="2" OperandType="Tag" Operator="and">
<Oprand ID="283839" Name="reviews" />
<Oprand ID="697348" Name="retention" />
</SubExpression>
<SubExpression ID="3" OperandType="Tag" Operator="not">
<Oprand ID="281556" Name="richfaces" />
<Oprand ID="2993766" Name="rgp" />
</SubExpression>
</Expression>'

二,XPath路径表达式

XPath 使用路径表达式在 XML 文档中选取节点,节点是通过沿着路径选取的,XPath是查询XML数据时必备的参数。

常用的路径表达式是:

  • .   :选取当前节点;
  • ..  :选取当前节点的父节点;
  • /   :从根节点开始;
  • //  :从匹配选择的节点开始选取,而不考虑其位置;
  • *  :通配符,匹配任意字符,或任意节点;
  • node()  :匹配任意节点,跟通配符 * 功能相似;
  • @PropertyName :选取属性;

在路径表达式中,跟节点的选取有关的表达式是:

  • NodeName:选取指定节点名及其所有子节点;
  • NodeName[N]:选取指定节点集合的第N个节点;
  • NodeName[@Name]:选取当前节点中带有指定属性的节点;

三,XML数据的查询(query()函数

@xml.query(’xpath‘)函数,参数是路径表达式,返回XML数据类型的结果,该XML是非类型化(untyped)的。

1,选取节点及其子节点

示例中,从根节点Expression开始,选取SubExpression节点及其子节点:

select @xml.query('/Expression/SubExpression')

query()函数返回的结果如下,该查询结果是非类型化的XML数据。

<SubExpression ID="1" OperandType="Tag" Operator="and">
<Oprand ID="268819" Name="abuse" />
<Oprand ID="277029" Name="mongohq" />
<Oprand ID="516813" Name="access" />
</SubExpression>
<SubExpression ID="2" OperandType="Tag" Operator="and">
<Oprand ID="283839" Name="reviews" />
<Oprand ID="697348" Name="retention" />
</SubExpression>
<SubExpression ID="3" OperandType="Tag" Operator="not">
<Oprand ID="281556" Name="richfaces" />
<Oprand ID="2993766" Name="rgp" />
</SubExpression>

2,选取指定节点的所有子节点集合

select @xml.query('/Expression/SubExpression/node()')
select @xml.query('/Expression/SubExpression/*')

结果集是SubExpression节点下的所有子节点:

<Oprand ID="268819" Name="abuse" />
<Oprand ID="277029" Name="mongohq" />
<Oprand ID="516813" Name="access" />
<Oprand ID="283839" Name="reviews" />
<Oprand ID="697348" Name="retention" />
<Oprand ID="281556" Name="richfaces" />
<Oprand ID="2993766" Name="rgp" />

四,XML数据的查询(value()函数)

@xml.value('xpath','sql_data_type'),返回XML数据中单个属性的标量值,在使用value()函数时,xpath 参数必须指定返回的是单个值,而value()函数不会去check返回值的数量。

一般情况下,即使xml数据只有一个属性值,静态类型化(Static typing)要求,xpath表达式也必须显式指定返回单个标量值,因此,必须指定在xpath函数的末尾添加”[1]“,通常的xpath表达式是”(xpath)[1]“。

select @xml.value('(/Expression/SubExpression[1]/@ID)[1]','int')
select @xml.value('(/Expression/SubExpression/@ID)[1]','int')

五,XML数据的查询(nodes()函数) 

@xml.nodes ('xpath') 函数返回节点的集合,用于把XML数据转换为关系数据表,返回的每一个行都是XML数据类型,语法是:

nodes ('xpath') as table(column)  

通过nodes()函数,返回SubExpression节点及其属性,由于单个节点中,属性名不可能重复,因此,在nodes()函数返回的单个节点中,不需要通过xpath路由,直接获取当前节点的属性值,这样,可以在xpath表达式中直接指定属性,不需要显式以“[1]”结尾。

示例代码如下,在value()函数中,直接指定属性值,表示获取当前节点的属性值:

select t.v.query('.') as SubExpression
,t.v.value('@ID','int') as SubExpressionID
,t.v.value('@OperandType','varchar(16)') as OperandType
,t.v.value('@Operator','varchar(16)') as Operator
from @xml.nodes('/Expression/SubExpression') as t(v)

通过cross apply 连接操作,把SubExpression节点下的所有数据都转换为关系型数据,并把该数据存储到临时数据表#Expressions中:

;with cte_Expressions as
(
select e.v.query('.') as Expression
,e.v.value('@ID','int') as ExpressionID
,e.v.value('@TaxonomyID','int') as TaxonomyID
from @xml.nodes('/Expression') as e(v)
)
,cte_SubExpression as
(
select e.ExpressionID
,e.TaxonomyID
,se.SubExpression
,se.SubExpressionID
,se.OperandType
,se.Operator
from cte_Expressions e
cross apply
(
select t.v.query('.') as SubExpression
,t.v.value('@ID','int') as SubExpressionID
,t.v.value('@OperandType','varchar(16)') as OperandType
,t.v.value('@Operator','varchar(16)') as Operator
from e.Expression.nodes('/Expression/SubExpression') as t(v)
) as se
)
select p.TaxonomyID
,p.ExpressionID
,p.SubExpressionID
,p.OperandType
,p.Operator
,d.OperandID
,d.OperandName
from cte_SubExpression p
cross apply
(
select t.v.value('@ID','int') as OperandID
,t.v.value('@Name','varchar(32)') as OperandName
from p.SubExpression.nodes('/SubExpression/Oprand') as t(v)
) as d

六,把行集数据转化为XML数据(for xml path)

把行集数据转化为XML数据,需要用到for xml path子句,该子句的特点是:

  • path('root') 子句用于指定根节点;
  • select子句的字段别名用于指定属性,别名中必须使用@符号标识出属性名,例如:'@PropertyName';
  • 在select 子句中,如果不在别名中把字段指定为属性,那么该字段的值作为节点值,节点值分为标量类型和XML类型;
    • 对于标量类型,节点值是标量值;
    • 对于XML类型,节点值是子节点的集合;

例如,要把数据转换为如下的关系型数据结构,其SubExpression字段是一个非类型化的XML数据,要完成这样的数据转换,必须使用for xml path子句和cast()类型转换:

<SubExpression ID="1" OperandType="Tag" Operator="not">
<Oprand ID="268819" Name="abuse" />
<Oprand ID="277029" Name="mongohq" />
<Oprand ID="516813" Name="access" />
</SubExpression>
<SubExpression ID="2" OperandType="Tag" Operator="not">
<Oprand ID="283839" Name="reviews" />
<Oprand ID="697348" Name="retention" />
</SubExpression>
<SubExpression ID="3" OperandType="Tag" Operator="not">
<Oprand ID="281556" Name="richfaces" />
<Oprand ID="2993766" Name="rgp" />
</SubExpression>

使用类型转换的目的,是为了把for xml path返回的字符串转换成XML数据类型,这样,就能以XML格式嵌入到上次的for xml path的结构中,作为子节点:

;with cte_Expressions as
(
select distinct ExpressionID
,TaxonomyID
from #Expressions with(nolock)
)
,cte_SubExpressions as
(
select o.ExpressionID
,o.SubExpressionID
,o.OperandType
,o.Operator
from #Expressions o with(nolock)
group by o.ExpressionID
,o.SubExpressionID
,operandType
,o.Operator
)
select e.TaxonomyID as TaxonomyID
,e.ExpressionID as ExpressionID
,cast(
(
select o.SubExpressionID as '@ID'
,o.OperandType as '@OperandType'
,case o.Operator when '&' then 'and' else 'not' end as '@Operator'
,cast((
select op.OperandID as '@ID'
,op.OperandName as '@Name'
from #Expressions op with(nolock)
where o.ExpressionID=op.ExpressionID
and o.SubExpressionID=op.SubExpressionID
for xml path('Oprand')
)as xml)
from cte_SubExpressions o
where o.ExpressionID=e.ExpressionID
for xml path('SubExpression')
) as xml)
as SubExpressions
from cte_Expressions e

还有两个函数modify()和exist(),用于XML数据的修改和检查,由于在我当前接触的项目中,没有用到过,我就不写了。

到此,文章也该结尾了,XML的极简用法已经总结了很多,在以后工作中国,如果用到XML时,翻开这篇文章,能够快速解决XML常见的数据查询和解析问题,这样就足够了。

参考文档:

Use PATH Mode with FOR XML

XML Data (SQL Server)

最简单的XML用法的更多相关文章

  1. 非常简单的XML解析(SAX解析、pull解析)

    这里只是把解析的数据当日志打出来了 非常简单的xml解析方式 package com.example.demo.service; import java.io.IOException; import ...

  2. oc-12-NSString 类简单介绍及用法

    // 11-[掌握]NSString 类简单介绍及用法 #import <Foundation/Foundation.h> int main(int argc, const char * ...

  3. 一个简单的XML与数组之间的转换

    xml是网络使用最多的数据交换格式,所以,不掌握怎么操作它,又有蛋疼的了. php中可以操作xml的类/函数很多,个人认为最简单的是SimpleXMLElement这个类,它的使用就跟其名字一样:简单 ...

  4. 最简单的XML转数组

    /** * 最简单的XML转数组 * @param string $xmlstring XML字符串 * @return array XML数组 */ function simplest_xml_to ...

  5. MyBatis 使用简单的 XML或注解用于配置和原始映射

    MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis .My ...

  6. C实现简单的xml格式文件

    今天在工作中遇到了一个需要处理xml格式的字符串,需求是修改某个固定的value值,并且还要把这个xml的key和value按照原本的格式在推送回去. 如果使用库,就显得太臃肿了,就想写个简单的dem ...

  7. JMS学习篇《一》ActiveMQ消息中间件的简单介绍与用法-概念篇

    原创说明:本篇博文为本人原创作品,转载请注明出处 1.何为消息中间件 消息中间件是一种在分布式应用中互相交换信息的一种技术,常见的成熟消息中间件有:RabbitMQ.SonicMQ,activeMQ. ...

  8. STRANS一:简单的XML转换

    心情不好,泥总把表妹微信给冰冰了,心塞... 1.简单的单层结构: <?sap.transform simple?> <tt:transform xmlns:tt="htt ...

  9. spring mvc简单介绍xml版

    spring mvc介绍:其实spring mvc就是基于servlet实现的,只不过他讲请求处理的流程分配的更细致而已. spring mvc核心理念的4个组件: 1.DispatcherServl ...

随机推荐

  1. MSSQL在线文件还原脚本

    在线文件还原:如果比较大的MSSQL数据库的损坏只是集中在其中某一个文件或者文件组上,使用在线文件还原技术,只是把坏掉的数据文件或者文件组重建,能节约很多时间.以下是测试脚本(假设损坏的文件时Trn0 ...

  2. scp 利用 ssh 协议 复制文件

    有时候,我们使用 scp 命令可以解决我们很多问题: NAME scp — secure copy (remote file copy program) 使用举例:拷贝当前文件,到 系统 10.66. ...

  3. Gmail 设置,时区

    问题提出: 我们工作的时候,需要和不同时区的人进行合作.我们需要注意时区问题.如果没有设置好时区,会造成很多不便. 了解时区问题: 通过 这个网站可以,让你对时区有所了解:http://zh.thet ...

  4. elasticsearch报错之 memory locking requested for elasticsearch process but memory is not locked

    安装elasticsearch报错如下: [2019-01-14T03:57:16,453][ERROR][o.e.b.Bootstrap ] [ip-172-31-30-62.ec2.interna ...

  5. MongoDB中_class字段的作用

    我们知道,如果你用Java的Sping Data 框架映射Pojo为MongoDB数据时,数据库中会自动给你添加一个_class字段,那这个字段是干嘛用的呢?我们可以不可以不要这个字段呢? 直接上结论 ...

  6. 【js】实现继承的6种方法

    1.原型链 基本思想:利用原型链让一个引用类型继承另一个引用类型的属性和方法. 让原型对象(B.prototype)等于另一个类型的实例(new A()), 即B.prototype = new A( ...

  7. node学习笔记_01 环境搭建

    一.下载安装nvm (node版本管理器),方便以后版本切换 nvm list            -> 查看node版本(版本最好在8.0以上,不然在vsCode断点调试进不去,跟node版 ...

  8. python第三十二课——栈

    栈:满足特点 --> 先进后出,类似于我们生活中的子弹夹 [注意] 对于栈结构而言:python中没有为其封装特定的函数,我们可以使用list(列表)来模拟栈的特点 使用list对象来模拟栈结构 ...

  9. $Gauss$消元

    $Gauss$消元 今天金牌爷来问我一个高消的题目,我才想起来忘了学高消... 高斯消元用于解线性方程组,也就是形如: $\left\{\begin{matrix}a_{11}x_1+a_{12}x_ ...

  10. Django使用静态文件

    除了由服务器生成的HTML文件外,网页应用一般需要提供其它必要的文件 —— 比如图片文件.JavaScript脚本和CSS样式表 —— 来为用户呈现出一个完整的网站. 在Django中,我们将这些文件 ...