我需要对 Oracle 数据库上的表执行一些查询。该表包含 XML 值。下面是我的 xml 结构示例:
<Parent>
<ParentId>382010</ParentId>
<LastUpd>2023-03-01T22:59:10.456241</LastUpd>
<UserId>0</UserId>
<attrn>xxx</attrn>
<Child>
<ChildId>1</ChildId>
<Attribute1 ID="1873" D="1466 Description">1466</Attribute1>
<Attribute2 ID="1234" D="QWERTY Description" E="503" ED="Error 503 Description">QWERTY</Attribute2>
<Attribute3 ID="4921" D="Other Description">YourValue</Attribute3>
</Child>
<Child>
<ChildId>2</ChildId>
<Attribute1 ID="1296" D="Some Description">1234</Attribute1>
<Attribute2 ID="1234" D="Some Different Description">ABC</Attribute2>
<Attribute3 ID="4921" D="Other Description" E="501" ED="Error 501 Description">MyValye</Attribute3>
</Child>
</Parent>
我需要选择所有有错误的孩子属性,可以通过“E”属性来识别(例如孩子1中的属性2和孩子2中的属性3)。
我对这种 XML 查询不是很专家,经过一番尝试后,我最终构建了下面的查询:
SELECT EXTRACTVALUE (VALUE (X), '/Parent/UserId') AS USER_ID
,EXTRACTVALUE (VALUE (X), '/Parent/ParentId') AS PARENT_ID
,EXTRACTVALUE (VALUE (X), '/Parent/attrn') AS PARENT_ATTR_N_COL_NAME
,EXTRACTVALUE (VALUE (I), '/Child/ChildId') AS ROW_NUM
,CASE
WHEN EXISTSNODE (VALUE (E), '/Attribute1/@E') = 1 THEN ATTR_ONE_COL_NAME
WHEN EXISTSNODE (VALUE (E), '/Attribute2/@E') = 1 THEN ATTR_TWO_COL_NAME
WHEN EXISTSNODE (VALUE (E), '/Attribute3/@E') = 1 THEN ATTR_THREE_COL_NAME
END AS FIELD
,EXTRACTVALUE (VALUE(E), '/*/text()') as VALUE
,EXTRACTVALUE (VALUE(E), '/*/@E') as ERROR_CODE
,EXTRACTVALUE (VALUE(E), '/*/@ED') as ERROR_DESC
FROM XML_TABLE X
,TABLE (XMLSEQUENCE (EXTRACT (VALUE (X), '/Parent/Child'))) I
,TABLE (XMLSEQUENCE (EXTRACT (VALUE (I), '/Child/*'))) E
WHERE EXTRACTVALUE (VALUE (X), '/Parent/ParentId') = 382010
AND EXISTSNODE (VALUE (E), '/*/@E') = 1';
当然,XML(然后是查询)实际上要大得多,我只是发布了一个简化的示例以使其更加综合。有时父节点包含数百个子节点,有时我需要查询数千个父节点,所以我想知道是否有一种方法可以优化上述查询并使其更快。
非常感谢:)
考虑用更新且更强大的
EXTRACTVALUE
替换已弃用的
XMLTABLE
函数。使用 XMLTABLE
,所有解析都可以在一次查询中完成。然后,您可以使用该查询来执行 DML 操作,也可以循环遍历它以进行更多的过程控制。
下面的代码与您的结果并不完全匹配,但它足够接近,可以让您了解它是如何工作的。
select
parent.user_id, parent.parent_id, parent.parent_attr_n_col_name,
child.row_num, attribute.value, attribute.error_code, attribute.error_desc
from xmltest x,
xmltable
('/Parent' passing x.a columns
user_id number path 'UserId',
parent_id number path 'ParentId',
parent_attr_n_col_name path 'attrn'
) parent,
xmltable
('/Parent/Child' passing x.a columns
row_num number path 'ChildId'
) child,
xmltable
('/Parent/Child/*' passing x.a columns
value varchar2(100) path 'text()',
error_code varchar2(100) path '@E',
error_desc varchar2(100) path '@ED'
) attribute
where parent_id = 382010
and error_code is not null;
这是我用于测试代码的架构:
--drop table xmltest;
create table xmltest(a xmltype);
insert into xmltest values(xmltype('
<Parent>
<ParentId>382010</ParentId>
<LastUpd>2023-03-01T22:59:10.456241</LastUpd>
<UserId>0</UserId>
<attrn>xxx</attrn>
<Child>
<ChildId>1</ChildId>
<Attribute1 ID="1873" D="1466 Description">1466</Attribute1>
<Attribute2 ID="1234" D="QWERTY Description" E="503" ED="Error 503 Description">QWERTY</Attribute2>
<Attribute3 ID="4921" D="Other Description">YourValue</Attribute3>
</Child>
<Child>
<ChildId>2</ChildId>
<Attribute1 ID="1296" D="Some Description">1234</Attribute1>
<Attribute2 ID="1234" D="Some Different Description">ABC</Attribute2>
<Attribute3 ID="4921" D="Other Description" E="501" ED="Error 501 Description">MyValye</Attribute3>
</Child>
</Parent>'));
这是您的查询,根据之前的架构进行了一些对我有用的更改。我不确定你的表是如何设置的 - 使用 REF 而不是值?
SELECT
EXTRACTVALUE (X.a, '/Parent/UserId') AS USER_ID
,EXTRACTVALUE (X.a, '/Parent/ParentId') AS PARENT_ID
,EXTRACTVALUE (X.a, '/Parent/attrn') AS PARENT_ATTR_N_COL_NAME
,EXTRACTVALUE (I.column_value, '/Child/ChildId') AS ROW_NUM
-- ,CASE
-- WHEN EXISTSNODE (E.column_value, '/Attribute1/@E') = 1 THEN ATTR_ONE_COL_NAME
-- WHEN EXISTSNODE (E.column_value, '/Attribute2/@E') = 1 THEN ATTR_TWO_COL_NAME
-- WHEN EXISTSNODE (E.column_value, '/Attribute3/@E') = 1 THEN ATTR_THREE_COL_NAME
-- END AS FIELD
,EXTRACTVALUE (E.column_value, '/*/text()') as VALUE
,EXTRACTVALUE (E.column_value, '/*/@E') as ERROR_CODE
,EXTRACTVALUE (E.column_value, '/*/@ED') as ERROR_DESC
FROM xmltest X
,TABLE (XMLSEQUENCE (EXTRACT (X.a, '/Parent/Child'))) I
,TABLE (XMLSEQUENCE (EXTRACT (value(I), '/Child/*'))) E
WHERE EXTRACTVALUE (X.a, '/Parent/ParentId') = 382010
AND EXISTSNODE (VALUE (E), '/*/@E') = '1';