我想在XML文档上有一条select语句,并且一栏应该返回我 每个节点 的 路径 。
例如,给定数据
SELECT * FROM TABLE(XMLSequence( XMLTYPE('<?xml version="1.0"?> <users><user><name>user1</name></user> <user><name>user2</name></user> <group> <user><name>user3</name></user> </group> <user><name>user4</name></user> </users>').extract('/*//*[text()]'))) t;
导致
column_value -------- <user><name>user1</name></user> <user><name>user2</name></user> <user><name>user3</name></user> <user><name>user4</name></user>
我想要这样的结果:
path value ------------------------ -------------- /users/user/name user1 /users/user/name user2 /users/group/user/name user3 /users/user/name user4
我看不到如何做到这一点。我认为必须正确地进行两件事:
path
XMLType
<users><group><user><name>user3</name></user></group></user>
<user><name>user3</name></user>
也许我还没有XMLType完全理解。可能是我需要一种不同的方法,但我看不到它。
旁注:
null
value
text()
"name"
('/users/groups/user', 'name', 'user3')
您可以通过Oracle XML DB XQuery函数集中的XMLTable函数来实现:
select * from XMLTable( ' declare function local:path-to-node( $nodes as node()* ) as xs:string* { $nodes/string-join(ancestor-or-self::*/name(.), ''/'') }; for $i in $rdoc//name return <ret><name_path>{local:path-to-node($i)}</name_path>{$i}</ret> ' passing XMLParse(content ' <users><user><name>user1</name></user> <user><name>user2</name></user> <group> <user><name>user3</name></user> </group> <user><name>user4</name></user> </users>' ) as "rdoc" columns name_path varchar2(4000) path '//ret/name_path', name_value varchar2(4000) path '//ret/name' )
对我来说,XQuery看起来至少比XSLT更直观地处理XML数据。
您可以在此处找到有用的XQuery函数集。
更新1
我想您需要在最后阶段使用完全数据的完全纯数据集。可以通过以下逐步构建的复杂方法来实现此目标,但是此变体非常耗资源。我建议审查最终目标(选择一些特定记录,计数元素数量等),然后简化此解决方案或完全更改它。
更新2
除最后一个步骤外,所有其他步骤均从此更新中删除,因为@ABCade在注释中提出了更为优雅的解决方案。下面的 Update 3 部分提供了此解决方案。
第1步 -使用相应的查询结果构建ID的数据集
第2步 -汇总到单个XML行
步骤3- 通过使用XMLTable查询约束的XML,最终获得完整的数据集
with xmlsource as ( -- only for purpose to write long string only once select ' <users><user><name>user1</name></user> <user><name>user2</name></user> <group> <user><name>user3</name></user> </group> <user><name>user4</name></user> </users>' xml_string from dual ), xml_table as ( -- model of xmltable select 10 id, xml_string xml_data from xmlsource union all select 20 id, xml_string xml_data from xmlsource union all select 30 id, xml_string xml_data from xmlsource ) select * from XMLTable( ' for $entry_user in $full_doc/full_list/list_entry/name_info return <tuple> <id>{data($entry_user/../@id_value)}</id> <path>{$entry_user/name_path/text()}</path> <name>{$entry_user/name_value/text()}</name> </tuple> ' passing ( select XMLElement("full_list", XMLAgg( XMLElement("list_entry", XMLAttributes(id as "id_value"), XMLQuery( ' declare function local:path-to-node( $nodes as node()* ) as xs:string* { $nodes/string-join(ancestor-or-self::*/name(.), ''/'') };(: function to construct path :) for $i in $rdoc//name return <name_info><name_path>{local:path-to-node($i)}</name_path><name_value>{$i/text()}</name_value></name_info> ' passing by value XMLParse(content xml_data) as "rdoc" returning content ) ) ) ) from xml_table ) as "full_doc" columns id_val varchar2(4000) path '//tuple/id', path_val varchar2(4000) path '//tuple/path', name_val varchar2(4000) path '//tuple/name' )
更新3
正如@ABCade在他的评论中提到的那样,将ID与XQuery结果联接起来确实是一种非常简单的方法。
因为我不喜欢答案中的外部链接,所以下面的代码代表了他的SQL提琴,有点适应了此答案中的数据源:
with xmlsource as ( -- only for purpose to write long string only once select ' <users><user><name>user1</name></user> <user><name>user2</name></user> <group> <user><name>user3</name></user> </group> <user><name>user4</name></user> </users>' xml_string from dual ), xml_table as ( -- model of xmltable select 10 id, xml_string xml_data from xmlsource union all select 20 id, xml_string xml_data from xmlsource union all select 30 id, xml_string xml_data from xmlsource ) select xd.id, x.* from xml_table xd, XMLTable( 'declare function local:path-to-node( $nodes as node()* ) as xs:string* {$nodes/string-join(ancestor-or-self::*/name(.), ''/'') }; for $i in $rdoc//name return <ret><name_path>{local:path-to-node($i)}</name_path>{$i}</ret> ' passing XMLParse(content xd.xml_data ) as "rdoc" columns name_path varchar2(4000) path '//ret/name_path', name_value varchar2(4000) path '//ret/name' ) x