sql - 如何在 Oracle 的 XMLTABLE 中包含对象列表?
问题描述
我有以下(最小)XML:
<root>
<person>
<name>Miguel Martins</name>
<age>32</age>
<list_of_numbers>
<number>1</number>
<number>2</number>
</list_of_numbers>
</person>
<person>
<name>Another Person</name>
<age>19</age>
<list_of_numbers>
<number>3</number>
<number>4</number>
</list_of_numbers>
</person>
</root>
以及以下查询:
with my_with_clause as
(select '
<root>
<person>
<name>Miguel Martins</name>
<age>32</age>
<list_of_numbers>
<number>1</number>
<number>2</number>
</list_of_numbers>
</person>
<person>
<name>Another Person</name>
<age>19</age>
<list_of_numbers>
<number>3</number>
<number>4</number>
</list_of_numbers>
</person>
</root>
' my_xml
from dual)
select t1.*
from my_with_clause,
xmltable('/root/person' passing xmltype(my_with_clause.my_xml) columns name path 'name', age path 'age') t1;
产生以下输出:
+----------------+-----+
| Name | Age |
+----------------+-----+
| Miguel Martins | 32 |
| Another Person | 19 |
+----------------+-----+
到目前为止,一切都很好。现在,我想将数字添加到别名为 T1 的表中。也就是说,我希望得到以下输出:
+----------------+-----+-------------+
| Name | Age | Some_Number |
+----------------+-----+-------------+
| Miguel Martins | 32 | 1 |
| Miguel Martins | 32 | 2 |
| Another Person | 19 | 3 |
| Another Person | 19 | 4 |
+----------------+-----+-------------+
我尝试将 some_number 列添加到 XMLTABLE。那是:
with my_with_clause as
(select '
<root>
<person>
<name>Miguel Martins</name>
<age>32</age>
<list_of_numbers>
<number>1</number>
<number>2</number>
</list_of_numbers>
</person>
<person>
<name>Another Person</name>
<age>19</age>
<list_of_numbers>
<number>3</number>
<number>4</number>
</list_of_numbers>
</person>
</root>
' my_xml
from dual)
select t1.*
from my_with_clause,
xmltable('/root/person' passing xmltype(my_with_clause.my_xml) columns name path 'name', age path 'age', some_number path 'list_of_numbers/number') t1;
但是,我没有得到想要的输出。相反,我收到以下错误:
ORA-19025: EXTRACTVALUE 仅返回一个节点的值
我怎样才能达到预期的输出?这是一个SQLFiddle供您试用(如有必要)。
解决方案
您可以使用链式 XMLTable 调用:
select t1.name, t1.age, t2.some_number
from my_with_clause
cross join xmltable (
'/root/person'
passing xmltype(my_with_clause.my_xml)
columns name varchar2(20) path 'name',
age number path 'age',
list_of_numbers xmltype path 'list_of_numbers/number'
) t1
cross join xmltable (
'/number'
passing t1.list_of_numbers
columns some_number number path '.'
) t2;
NAME AGE SOME_NUMBER
-------------------- ---------- -----------
Miguel Martins 32 1
Miguel Martins 32 2
Another Person 19 3
Another Person 19 4
SQL Fiddle 不喜欢这样,但db<>fiddle喜欢,并且它在本地针对 11gR2 工作。(实际上SQL Fiddle 可以使用真实表而不是 CTE ...)
或者
select t1.name, t1.age, t2.some_number
from my_with_clause
cross join xmltable (
'/root/person'
passing xmltype(my_with_clause.my_xml)
columns name varchar2(20) path 'name',
age number path 'age',
list_of_numbers xmltype path 'list_of_numbers'
) t1
cross join xmltable (
'/list_of_numbers/number'
passing t1.list_of_numbers
columns some_number number path '.'
) t2;
使用此 XML,您还可以使用一个 XMLTable 来执行此操作,方法是从数字开始,然后在节点上查找其他数据:
select t1.name, t1.age, t1.some_number
from my_with_clause
cross join xmltable (
'/root/person/list_of_numbers/number'
passing xmltype(my_with_clause.my_xml)
columns name varchar2(20) path './../../name',
age number path './../../age',
some_number number path '.'
) t1;
NAME AGE SOME_NUMBER
-------------------- ---------- -----------
Miguel Martins 32 1
Miguel Martins 32 2
Another Person 19 3
Another Person 19 4
但是您真正的(不是最小的)XML 可能无法做到这一点。
这也适用于具有多行的表,而不仅仅是 CTE 或具有单个 XML 值要解包的表。
如果您可能遇到list_of_names
缺少或为空的情况,并且您仍想显示姓名/年龄,则可以使用外连接而不是交叉连接,但它需要一个丑陋的on 1=1
子句。SQL Fiddle显示了这种数据的交叉连接和左连接,但如果可以的话,我会避免使用左连接方法。
如果您使用的是 12c 或更高版本,则可以使用outer apply
而不是left join ... on 1=1
,这不太令人反感。(如果您不必担心缺少数字,您可以使用cross apply
而不是cross join
@Lukasz 显示的那样 - 在这里似乎没有什么区别。)
ORA-19025 很有趣。SQL Fiddle 正在运行 Oracle Database 11g Express Edition Release 11.2.0.2.0。在 Enterprise Edition 11.2.0.4 中,您的代码将获得
ORA-19279: XPTY0004 - XQuery 动态类型不匹配:预期的单例序列 - 得到多项目序列
相反,但这是同一个问题;每个人下面有多个number
节点,它不知道如何处理它们,作为默认数据类型 - 因为您没有指定数据类型,所以所有内容都作为字符串返回。在我的第一个版本中,我使用相同的路径,但将该列声明为 XMLType,因此您可以在第一个版本中将数字作为 XML 片段获得:
<number>1</number><number>2</number>
或者第二个:
<list_of_numbers><number>1</number><number>2</number></list_of_numbers>
然后这些可以被链接的 XMLTable 调用使用。
推荐阅读
- c - 从 float 转换为 double 并返回到 float(未优化)是否保证返回相同的值?
- javascript - vue.js 中的 Intersection Observer 问题
- javascript - 如何以 JSON 格式下载带有 blob 的 VUEX 商店
- javascript - JavaScript 以 (() => { 开头并以 })() 结尾
- google-apps-script - Google Apps Script V8 引擎是否支持 Promise?
- if-statement - 为什么使用三元运算符会出错?
- r - 在R中的一个因子中应用一长串标签
- python - 获取现有索引的 IndexError Traceback(最近一次调用最后一次)
- flutter - Flutter:如何检查开发者模式启用?
- ios - 如何在 Swift 中为 Firebase 文件上传完成异步后台任务