概述:在OceanBase 的Oracle模式支持XMLType特性后,OceanBase拥有了XML的数据存储、计算、分析能力。用户无需将XML数据作为文本存储,在业务代码中解析并操作XML文本,而是可以直接基于XML内置的能力,对XML数据进行存储、构造、增删改查。

1. 背景

1.1. XML的能力与应用场景

在数据库的描述配置、数据交换等场景中,XML(Extensible Markup Language)的身影屡见不鲜。这种数据交换格式基于文本,具有自解释性且标准化。因为企业用户的数据资产往往储存在关系数据库中,以二维表的形式进行逻辑存储,故而在数据传输与存储交换过程中,通常会产生以下需求:

  • 从关系数据库中查询的一个数据集合,转换为一个XML文档。
  • 提取XML文档的内容,以关系表的方式存储进数据库。
  • 直接在关系数据库中存储、处理XML文档。

同时,在此过程中,XML展现了它的五项能力。

能力1:数据组织结构

XML将数据按照树形结构组织,通过这种嵌套结构,XML可以存储非常大的数据集。

能力2:约束

XML提供两种约束方式,

  • 直接在单个XML文档中定义的约束称为DTD
  • 通过定义XSchema,来描述一系列XML文档的组织以及数据特征,其中Schema也是基于XML标准定义的一种特殊的XML文档。

能力3:查询。

最简单的Xpath类似于我们的文件路径,是访问树形结构最普遍的一种方式。XML标准赋予XPath更强大的能力,比如谓词过滤、基于函数的计算处理、复杂的查询等能力。

能力4:DML

XML标准定义了一系列DOM(The W3C Document Object Model)接口,提供了一套通用、跨平台的方法用于Get/Change/Add/Delete XML

能力5:XQuery

这是XML特有的一种操作语言,有点类似数据库的PL。其能力不仅仅是"Query",也提供了对XML的DML操作,以及复杂的流程控制能力(比如循环、赋值、条件语句、跳转、排序、过滤等)。

1.2. OceanBase v4.2 XML Type特性支持

上述可见,XML涉及的能力非常多。它的标准也非常复杂。正因此,在OceanBase 4.2版本发布的Oracle XML兼容性特性无法做到非常齐全,暂只包括以下四个特性。

特性1:XMLType支持

用户可以定义数据为XML类型,通过PL或SQL操作XML数据。

特性2:基础函数

主要用于XML数据的构造/增删改查。

  • 构造:通过构造函数,可以将关系数据库中的数据集合,转换为XML。比如XMLParse、XMLElement、XMLAttributes、XMLAgg等函数。
  • 查询:查询函数支持基于Xpath的查询, 比如Extract。
  • 更新:通过更新函数可以对XML文档做增量修改,改变某个XML节点的数据,比如UpdateXML。
  • 格式转换:将数据库存储的XML数据格式转换为标准的XML文本,比如XMLSERIALIZE。

特性3:存储

支持原生的XML Binary存储,这是一种查询友好的XML存储格式,相较直接基于文本存储,会避免XML文档的解析,也会加速XML的查询。

特性4:索引

基于虚拟生成列可以在XML文档上建立索引。

部分XML标准以及Oracle XML的功能点不在OceanBase 4.2版本发布的范围内,比如:

  • XQuery
  • XMLDOM
  • XML Package

1.3. 基于数据库能力处理XML

在OceanBase 的Oracle模式支持XMLType特性后,OceanBase拥有了XML的数据存储、计算、分析能力。用户无需将XML数据作为文本存储,在业务代码中解析并操作XML文本,而是可以直接基于XML内置的能力,对XML数据进行存储、构造、增删改查。

基于数据库能力处理XML的优势在于三点。

第一,增量查询/更新。数据库精确返回、修改用户需要操作的XML子节点数据,避免客户端和数据库交换完整XML数据,降低网络开销。

第二,查询优化。基于文本处理XML,每次都需要对XML进行解析,有比较大的解析开销。基于XMLType存储的XML数据只需在入库的时候解析一次,后续查询均不用解析,而且存储格式对查询做了特殊优化。

第三,"想要即所得"。数据库内置了关系数据向XML的相互转换能力,只用简单的SQL即可基于数据库中的数据构造复杂的XML文档,客户端不用基于数据库数据做二次处理。

可以说,OceanBase 4.2版本基于XML基础能力的组合,可以满足绝大部分XML的数据处理场景,"麻雀虽小五脏俱全"。

2. 使用操作

2.1. 创建含有XmlType列的表

# 创建含有xmltype列的表
OceanBase(SYS@SYS)>create table xml_t(id number, c1 xmltype);
Query OK, 0 rows affected (0.522 sec)

OceanBase(SYS@SYS)>desc xml_t;
+-------+---------+------+-----+---------+-------+
| FIELD | TYPE    | NULL | KEY | DEFAULT | EXTRA |
+-------+---------+------+-----+---------+-------+
| ID    | NUMBER  | YES  | NULL | NULL    | NULL  |
| C1    | XMLTYPE | YES  | NULL | NULL    | NULL  |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.079 sec)

2.2. XmlType的DML操作

#写入数据,可以直接使用合法的xml文本
OceanBase(SYS@SYS)>insert into xml_t values(1, '<?xml version="1.0" encoding="UTF-8" ?>
    '> <employee id="1">
    '> <name>Alice</name>
    '> <age>25</age>
		'> <empdate>2019-03-14</empdate>
    '> </employee>');
Query OK, 1 row affected (0.017 sec)

# 或是借助xmlparse表达式显式的将文本解析成xmltype数据后插入xmltype列
OceanBase(SYS@SYS)>insert into xml_t values(2, xmlparse(document '<?xml version="1.0" encoding="UTF-8" ?>
    '> <employee id="2">
    '> <name>Bob</name>
    '> <age>30</age>
    '> <empdate>2010-01-01</empdate>
    '> </employee>'));
Query OK, 1 row affected (0.006 sec)

#更新xmltype数据的方式与其它数据类型类似,同样使用update语句,例如将Bob的入职日期改为2010年2月1日
OceanBase(SYS@SYS)>update xml_t set c1=xmlparse(document '<?xml version="1.0" encoding="UTF-8" ?>
    '> <employee id="2">
    '>   <name>Bob</name>
    '>   <age>30</age>
    '>   <empdate>2010-02-01</empdate>
    '> </employee>') where id = 2;
Query OK, 1 row affected (0.010 sec)
Rows matched: 1  Changed: 1  Warnings: 02;

2.3. 查询XmlType数据

# 普通查询
OceanBase(SYS@SYS)>select c1 from xml_t;
+--------------------------------------------------------------------------------------------------------------------------------------------+
| C1                                                                                                                                         |
+--------------------------------------------------------------------------------------------------------------------------------------------+
| <?xml version="1.0" encoding="UTF-8"?>
<employee id="1">
  <name>Alice</name>
  <age>25</age>
  <empdate>2019-03-14</empdate>
</employee>
 |
| <?xml version="1.0" encoding="UTF-8"?>
<employee id="2">
  <name>Bob</name>
  <age>30</age>
  <empdate>2010-02-01</empdate>
</employee>
   |
+--------------------------------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.005 sec)

# 也可通过pl方法,getclobval() 和getstringval() 进行查询,查询到的内容不会被改变,
# 但返回类型变为CLOB或者VARCHAR2,注意使用此种方法时,必须使用表别名

select t.c1.getclobval() from xml_t t;
+--------------------------------------------------------------------------------------------------------------------------------------------+
| T.C1.GETCLOBVAL()                                                                                                                          |
+--------------------------------------------------------------------------------------------------------------------------------------------+
| <?xml version="1.0" encoding="UTF-8"?>
<employee id="1">
  <name>Alice</name>
  <age>25</age>
  <empdate>2019-03-14</empdate>
</employee>
 |
| <?xml version="1.0" encoding="UTF-8"?>
<employee id="2">
  <name>Bob</name>
  <age>30</age>
  <empdate>2010-02-01</empdate>
</employee>
   |
+--------------------------------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.260 sec)

2.4. 表达式

2.4.1. 本次特性支持的XML表达式总览

表达式名称功能简述
XMLPARSE解析字符串输入,如果是合法,则将其转换为xmltype数据
XMLELEMENT依据输入参数构造一个xmlelement,返回xmltype数据
XMLATTRIBUTES依据输入参数构造xmlattributes,只能作为xmlement表达式的输入,不能单独使用
XMLAGG聚合函数,将多个XML片段聚合成一个xmltype数据
EXTRACT依据Xpath抽取一个XML片段,其结果类型是XMLTYPE
EXTRACTVALUE依据Xpath抽取一个XML片段,默认返回类型为VARCHAR2(4000)
XMLSERIALIZE将XMLType数据序列化为varchar2或者clob,可通过参数进行格式化
XMLCAST抽取XML文档的内容(不包括element name),并将其转换为用户指定的内容
UPDATEXML替换XMLType文档中,Xpath指定的部分内容

EXTRACT,EXTRACTVALUE,UPDATEXML 需要指定Xpath,Xpath用来访问XML数据中心,特定的元素或属性。OB当前支持了XPath 1.0的大部分的location path能力,和一部分filter,function能力。

本次发布尚未支持的常用表达式有:

表达式名称功能简述
XMLTABLE将XML文档展开成一张关系表
XMLEXIST用来判断某个Xpath指定的路径在XML数据中是否存在
XMLISVALID校验XML文档是否符合XMLSchema的定义
XMLQUERY用来执行xquery表达式

2.4.2. 构造XML数据的表达式

2.4.2.1. XMLPARSE

XMLPARSE 表达式用于将一个字符串进行解析,如果是合法的XML文本,则将其转换为XMLType数据并返回。

# 解析document
OceanBase(SYS@SYS)>SELECT XMLPARSE(DOCUMENT '<?xml version="1.0" encoding="UTF-8" ?>
    '> <employee id="1">
    '>     <name>Alice</name>
    '>     <age>25</age>
    '>     <empdate>2019-03-14</empdate>
    '> </employee>') AS PO FROM DUAL;
+--------------------------------------------------------------------------------------------------------------------------------------------+
| PO                                                                                                                                         |
+--------------------------------------------------------------------------------------------------------------------------------------------+
| <?xml version="1.0" encoding="UTF-8"?>
<employee id="1">
  <name>Alice</name>
  <age>25</age>
  <empdate>2019-03-14</empdate>
</employee>
 |
+--------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.004 sec)
2.4.2.2. XMLELEMENT

XMLELEMENT 表达式用于构造一个XMLELEMENT,可以指定ELEMENT名称,属性以及内容,此方法用于将关系表数据转换成XML类型。

2.4.2.3. XMLATTRIBUTES

XMLATTRIBUTES 表达式用于构造XML 属性,只能作为XMLELEMENT表达式的输入。

OceanBase(SYS@SYS)>create table employees(name varchar2(20), empdate varchar2(20));
Query OK, 0 rows affected (0.161 sec)

OceanBase(SYS@SYS)>insert into employees values('Tom', '2020-01-01');
Query OK, 1 row affected (0.027 sec)

OceanBase(SYS@SYS)>insert into employees values('Jerry', '2020-02-01');
Query OK, 1 row affected (0.002 sec)

OceanBase(SYS@SYS)>select xmlelement(emp, xmlattributes(name), empdate) as "result" from employees;
+------------------------------------+
| result                             |
+------------------------------------+
| <EMP NAME="Tom">2020-01-01</EMP>   |
| <EMP NAME="Jerry">2020-02-01</EMP> |
+------------------------------------+
2 rows in set (0.008 sec)
2.4.2.4. XMLAGG

XMLAGG 用于将多个XML数据汇聚成单个XML数据,例如,在XMLELEMENT和XMLATTRIBUTES的例子中,我们将基础类型的employees表中的两行数据构造成了两条XML,这里可以使用XMLAGG将其汇聚。

OceanBase(SYS@SYS)>select xmlagg(xmlelement(emp, xmlattributes(name), empdate)) as "result" from employees;
+--------------------------------------------------------------------+
| result                                                             |
+--------------------------------------------------------------------+
| <EMP NAME="Tom">2020-01-01</EMP><EMP NAME="Jerry">2020-02-01</EMP> |
+--------------------------------------------------------------------+
1 row in set (0.007 sec)

2.4.3. 查询XML数据的表达式

2.4.3.1. EXTRACT

EXTRACT表达式,用于选取的XPath指定的内容,其返回值也是XMLType的数据;

OceanBase(SYS@SYS)>select id, extract(c1, '/employee/name') from xml_t;
+------+------------------------------+
| ID   | EXTRACT(C1,'/EMPLOYEE/NAME') |
+------+------------------------------+
|    1 | <name>Alice</name>
          |
|    2 | <name>Bob</name>
            |
+------+------------------------------+
2 rows in set (0.006 sec)/name') from xml_t;
2.4.3.2. EXTRACTVALUE

EXTRACTVALUE表达式与EXTRACT表达式类似,也选取XPath指定的内容,但其返回值是varchar2类型的数据,只会返回XPath指定路径的内容,不包括Element tag:

OceanBase(SYS@SYS)>select id, extractvalue(c1, '/employee/name') from xml_t;
+------+-----------------------------------+
| ID   | EXTRACTVALUE(C1,'/EMPLOYEE/NAME') |
+------+-----------------------------------+
|    1 | Alice                             |
|    2 | Bob                               |
+------+-----------------------------------+
2 rows in set (0.006 sec)
2.4.3.3. XMLSERIALIZE

XMLSERIALIZE用于将XMLType的数据转换为CLOB,BLOB或者VARCHAR2类型的数据,此表达式可以控制XML中各个元素的换行或者缩进行为。

OceanBase(SYS@SYS)>select xmlserialize(document c1 as varchar2(200) no indent) as res from xml_t where id=1;
+--------------------------------------------------------------------------------------------------------------------------------------+
| RES                                                                                                                                  |
+--------------------------------------------------------------------------------------------------------------------------------------+
| <?xml version="1.0" encoding="UTF-8"?>
<employee id="1">
<name>Alice</name>
<age>25</age>
<empdate>2019-03-14</empdate>
</employee>
 |
+--------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.007 sec)
2.4.3.4. XMLCAST

用于将XML中的内容转换为其它类型,例如NUMBER、VARCHAR2、CHAR、CLOB、BLOB及任何日期时间数据类型。

# number
OceanBase(SYS@SYS)>select xmlcast(xmlparse(CONTENT '<a>123.01</a>') as number) from dual;
+---------------------------------------------------+
| XMLCAST(XMLPARSE(CONTENT'<A>123.01</A>')ASNUMBER) |
+---------------------------------------------------+
|                                            123.01 |
+---------------------------------------------------+
1 row in set (0.004 sec)

# decimal
OceanBase(SYS@SYS)>select xmlcast(xmlparse(CONTENT '<a>123.01</a>') as decimal) from dual;
+----------------------------------------------------+
| XMLCAST(XMLPARSE(CONTENT'<A>123.01</A>')ASDECIMAL) |
+----------------------------------------------------+
|                                                123 |
+----------------------------------------------------+
1 row in set (0.005 sec)

# timestamp
OceanBase(SYS@SYS)>select xmlcast(xmlparse(CONTENT '<a>2023-04-03 15:13:00</a>') as timestamp) as res from dual;
+----------------------------+
| RES                        |
+----------------------------+
| 2023-04-03 15:13:00.000000 |
+----------------------------+
1 row in set (0.004 sec)

# date
OceanBase(SYS@SYS)>select xmlcast(xmlparse(CONTENT '<a>2023-04-03 15:13:00</a>') as date) as res from dual;
+---------------------+
| RES                 |
+---------------------+
| 2023-04-03 15:13:00 |
+---------------------+
1 row in set (0.004 sec)

2.4.4. 修改XML数据的表达式

2.4.4.1. UpdateXML

使用Update表达式,可以部分更新XML数据内的内容:

OceanBase(SYS@SYS)>SELECT c1 FROM xml_t WHERE id = 2;
+------------------------------------------------------------------------------------------------------------------------------------------+
| C1                                                                                                                                       |
+------------------------------------------------------------------------------------------------------------------------------------------+
| <?xml version="1.0" encoding="UTF-8"?>
<employee id="2">
  <name>Bob</name>
  <age>30</age>
  <empdate>2010-02-01</empdate>
</employee>
 |
+------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.006 sec)


OceanBase(SYS@SYS)>SELECT UPDATEXML(c1, '/employee/empdate/text()','2010-03-01') FROM xml_t
    -> WHERE id = 2;
+------------------------------------------------------------------------------------------------------------------------------------------+
| UPDATEXML(C1,'/EMPLOYEE/EMPDATE/TEXT()','2010-03-01')                                                                                    |
+------------------------------------------------------------------------------------------------------------------------------------------+
| <?xml version="1.0" encoding="UTF-8"?>
<employee id="2">
  <name>Bob</name>
  <age>30</age>
  <empdate>2010-03-01</empdate>
</employee>
 |
+------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.007 sec)

3. 未来规划:支持XQuery、XPath、XSchema

本次XML特性在Oceanbase 4.2 商业版上发布,限Oracle租户使用。

当前XML 特性还不支持XQuery,对XPath的支持也还不全面,在后续版本中会持续补全这部分能力。未来也会考虑提供对XSchema的支持,以及基于XSchema的XML关系对象存储方式,关系对象存储可以提供更好的存储性能以及XML中片段的查询效率。

Logo

了解最新的技术洞察和前沿趋势,参与 OceanBase 定期举办的线下活动,与行业开发者互动交流

更多推荐