Oracle XML 多子节点查询
Oracle XML Query with multiple child nodes
我想实现对 XML 元素的查询,但找不到实现此目的的合适方法。
让我们考虑一下 table:
CREATE TABLE SUBSCRIPTIONS(
SUBSCRIPTION_ID NUMBER,
PARAMETERS_XML CLOB
);
我的 XML 看起来像:
<Model>
<Parameters>
<Parameter>
<userParameter>outlets</userParameter>
<Values>
<Value>45</Value>
<Value>676</Value>
<Value>1502</Value>
...
</Values>
</Parameter>
</Parameters>
</Model>
我正在尝试使用此查询从 XML、参数名称和参数值中获取每个订阅:
SELECT
s.SUBSCRIPTION_ID,
s.PARAMETERS_XML,
xt.*
FROM EFNTA.SUBSCRIPTIONS s,
XMLTABLE(
'/Model/Parameters/Parameter'
PASSING XMLTYPE(s.PARAMETERS_XML)
COLUMNS USER_PARAM VARCHAR2(100) PATH 'userParameter',
PARAM_VALUE VARCHAR2(1000) PATH '//*:Values',
CHECKED_LIST_EXCLUDE VARCHAR2(100) PATH '//*:CheckedListExclude'
) xt ORDER BY 1 DESC;
问题是当有多个值时,结果作为一个大的串联链返回:
有什么简单的方法可以用逗号、分号或其他任何方式分隔这些值吗? (我事先不知道值的数量)我找不到如何用 XPath 实现这个。
您可以将 Values
处理拆分为第二个 XMLTable:
SELECT
s.SUBSCRIPTION_ID,
xt1.USER_PARAM,
xt2.PARAM_VALUE,
xt1.CHECKED_LIST_EXCLUDE
FROM SUBSCRIPTIONS s
CROSS APPLY XMLTABLE(
'/Model/Parameters/Parameter'
PASSING XMLTYPE(s.PARAMETERS_XML)
COLUMNS USER_PARAM VARCHAR2(100) PATH 'userParameter',
PARAM_VALUES XMLTYPE PATH '//*:Values',
CHECKED_LIST_EXCLUDE VARCHAR2(100) PATH '//*:CheckedListExclude'
) xt1
OUTER APPLY XMLTABLE(
'/*:Values/Value'
PASSING xt1.PARAM_VALUES
COLUMNS PARAM_VALUE VARCHAR2(1000) PATH '.'
) xt2
ORDER BY 1 DESC;
SUBSCRIPTION_ID
USER_PARAM
PARAM_VALUE
CHECKED_LIST_EXCLUDE
553219
outlets
45
null
553219
outlets
1502
null
553219
outlets
676
null
然后您可以汇总各个值:
SELECT
s.SUBSCRIPTION_ID,
xt1.USER_PARAM,
LISTAGG(xt2.PARAM_VALUE, ',') WITHIN GROUP (ORDER BY xt2.PARAM_VALUE) as PARAM_VALUES,
xt1.CHECKED_LIST_EXCLUDE
FROM SUBSCRIPTIONS s
CROSS APPLY XMLTABLE(
'/Model/Parameters/Parameter'
PASSING XMLTYPE(s.PARAMETERS_XML)
COLUMNS USER_PARAM VARCHAR2(100) PATH 'userParameter',
PARAM_VALUES XMLTYPE PATH '//*:Values',
CHECKED_LIST_EXCLUDE VARCHAR2(100) PATH '//*:CheckedListExclude'
) xt1
OUTER APPLY XMLTABLE(
'/*:Values/Value'
PASSING xt1.PARAM_VALUES
COLUMNS PARAM_VALUE VARCHAR2(1000) PATH '.'
) xt2
GROUP BY
s.SUBSCRIPTION_ID,
xt1.USER_PARAM,
xt1.CHECKED_LIST_EXCLUDE
ORDER BY 1 DESC;
SUBSCRIPTION_ID
USER_PARAM
PARAM_VALUES
CHECKED_LIST_EXCLUDE
553219
outlets
1502,45,676
null
您的示例没有命名空间,因此通配符看起来没有必要(而且我通常会尽可能避免使用通配符),但我保留了它们以防您简化它。
我将第一个连接更改为 cross apply
,这意味着您仍然只能看到存在 parameters_xml
的行,就像您原来的一样;但是做了第二个 outer apply
所以即使没有值你仍然会看到参数名称。
我也忽略了 CHECKED_LIST_EXCLUDE
,因为示例中没有;如果它也可以有多个值,那么您可能需要为此做类似的事情。
尝试使用 string-join
函数:'string-join(Values//Value, ", ")'
.
您可以使用 FLOWR 将 XML 重写为在每个 Value
元素中包含尾随 ,
:
SELECT s.SUBSCRIPTION_ID,
s.PARAMETERS_XML,
xt.User_Param,
RTRIM(xt.Param_Value, ',') AS Param_Value,
xt.Checked_List_Exclude
FROM EFNTA.SUBSCRIPTIONS s
CROSS JOIN
XMLTABLE(
'copy $e := .
modify (
for $i in $e/Model/Parameters/Parameter/Values/Value
return replace node $i with <Value>{$i},</Value>
)
return $e/Model/Parameters/Parameter'
PASSING XMLTYPE(s.PARAMETERS_XML)
COLUMNS
USER_PARAM VARCHAR2(100) PATH 'userParameter',
PARAM_VALUE VARCHAR2(1000) PATH '//*:Values',
CHECKED_LIST_EXCLUDE VARCHAR2(100) PATH '//*:CheckedListExclude'
) xt
ORDER BY 1 DESC;
或者,按照 的建议,使用 string-join()
:
SELECT s.SUBSCRIPTION_ID,
s.PARAMETERS_XML,
xt.*
FROM EFNTA.SUBSCRIPTIONS s
CROSS JOIN
XMLTABLE(
'/Model/Parameters/Parameter'
PASSING XMLTYPE(s.PARAMETERS_XML)
COLUMNS
USER_PARAM VARCHAR2(100) PATH 'userParameter',
PARAM_VALUE VARCHAR2(1000) PATH 'Values/string-join(./Value/text(), ",")',
CHECKED_LIST_EXCLUDE VARCHAR2(100) PATH '//CheckedListExclude'
) xt
ORDER BY 1 DESC;
这两个输出:
SUBSCRIPTION_ID
PARAMETERS_XML
USER_PARAM
PARAM_VALUE
CHECKED_LIST_EXCLUDE
1
<Model
<Parameters>
<Parameter>
<userParameter>outlets</userParameter>
<Values>
<Value>45</Value>
<Value>676</Value>
<Value>1502</Value>
</Values>
</Parameter>
</Parameters>
</Model>
outlets
45,676,1502
null
db<>fiddle here
我终于这样处理了:
SELECT
s.SUBSCRIPTION_ID,
s.PARAMETERS_XML,
xt.USER_PARAM,
xt.CHECKED_LIST_EXCLUDE,
NVL(xt.PARAM_VALUE, xt.PARAM_VALUES) AS PARAM_VALUES
FROM EFNTA.SUBSCRIPTIONS s,
XMLTABLE(
'/Model/Parameters/Parameter'
PASSING XMLTYPE(s.PARAMETERS_XML)
COLUMNS USER_PARAM VARCHAR2(100) PATH 'userParameter',
CHECKED_LIST_EXCLUDE VARCHAR2(100) PATH '//*:CheckedListExclude',
PARAM_VALUE VARCHAR2(4000) PATH 'Value',
PARAM_VALUES VARCHAR2(4000) PATH 'string-join(Values//Value,";")'
) xt ORDER BY 1 DESC;
我不得不放另一列和一个 NVL,因为我注意到有时我在节点正下方有一个节点,并且有所需的输出:
感谢您的建议,我会保密的,以防万一:-)
我想实现对 XML 元素的查询,但找不到实现此目的的合适方法。
让我们考虑一下 table:
CREATE TABLE SUBSCRIPTIONS(
SUBSCRIPTION_ID NUMBER,
PARAMETERS_XML CLOB
);
我的 XML 看起来像:
<Model>
<Parameters>
<Parameter>
<userParameter>outlets</userParameter>
<Values>
<Value>45</Value>
<Value>676</Value>
<Value>1502</Value>
...
</Values>
</Parameter>
</Parameters>
</Model>
我正在尝试使用此查询从 XML、参数名称和参数值中获取每个订阅:
SELECT
s.SUBSCRIPTION_ID,
s.PARAMETERS_XML,
xt.*
FROM EFNTA.SUBSCRIPTIONS s,
XMLTABLE(
'/Model/Parameters/Parameter'
PASSING XMLTYPE(s.PARAMETERS_XML)
COLUMNS USER_PARAM VARCHAR2(100) PATH 'userParameter',
PARAM_VALUE VARCHAR2(1000) PATH '//*:Values',
CHECKED_LIST_EXCLUDE VARCHAR2(100) PATH '//*:CheckedListExclude'
) xt ORDER BY 1 DESC;
问题是当有多个值时,结果作为一个大的串联链返回:
有什么简单的方法可以用逗号、分号或其他任何方式分隔这些值吗? (我事先不知道值的数量)我找不到如何用 XPath 实现这个。
您可以将 Values
处理拆分为第二个 XMLTable:
SELECT
s.SUBSCRIPTION_ID,
xt1.USER_PARAM,
xt2.PARAM_VALUE,
xt1.CHECKED_LIST_EXCLUDE
FROM SUBSCRIPTIONS s
CROSS APPLY XMLTABLE(
'/Model/Parameters/Parameter'
PASSING XMLTYPE(s.PARAMETERS_XML)
COLUMNS USER_PARAM VARCHAR2(100) PATH 'userParameter',
PARAM_VALUES XMLTYPE PATH '//*:Values',
CHECKED_LIST_EXCLUDE VARCHAR2(100) PATH '//*:CheckedListExclude'
) xt1
OUTER APPLY XMLTABLE(
'/*:Values/Value'
PASSING xt1.PARAM_VALUES
COLUMNS PARAM_VALUE VARCHAR2(1000) PATH '.'
) xt2
ORDER BY 1 DESC;
SUBSCRIPTION_ID | USER_PARAM | PARAM_VALUE | CHECKED_LIST_EXCLUDE |
---|---|---|---|
553219 | outlets | 45 | null |
553219 | outlets | 1502 | null |
553219 | outlets | 676 | null |
然后您可以汇总各个值:
SELECT
s.SUBSCRIPTION_ID,
xt1.USER_PARAM,
LISTAGG(xt2.PARAM_VALUE, ',') WITHIN GROUP (ORDER BY xt2.PARAM_VALUE) as PARAM_VALUES,
xt1.CHECKED_LIST_EXCLUDE
FROM SUBSCRIPTIONS s
CROSS APPLY XMLTABLE(
'/Model/Parameters/Parameter'
PASSING XMLTYPE(s.PARAMETERS_XML)
COLUMNS USER_PARAM VARCHAR2(100) PATH 'userParameter',
PARAM_VALUES XMLTYPE PATH '//*:Values',
CHECKED_LIST_EXCLUDE VARCHAR2(100) PATH '//*:CheckedListExclude'
) xt1
OUTER APPLY XMLTABLE(
'/*:Values/Value'
PASSING xt1.PARAM_VALUES
COLUMNS PARAM_VALUE VARCHAR2(1000) PATH '.'
) xt2
GROUP BY
s.SUBSCRIPTION_ID,
xt1.USER_PARAM,
xt1.CHECKED_LIST_EXCLUDE
ORDER BY 1 DESC;
SUBSCRIPTION_ID | USER_PARAM | PARAM_VALUES | CHECKED_LIST_EXCLUDE |
---|---|---|---|
553219 | outlets | 1502,45,676 | null |
您的示例没有命名空间,因此通配符看起来没有必要(而且我通常会尽可能避免使用通配符),但我保留了它们以防您简化它。
我将第一个连接更改为 cross apply
,这意味着您仍然只能看到存在 parameters_xml
的行,就像您原来的一样;但是做了第二个 outer apply
所以即使没有值你仍然会看到参数名称。
我也忽略了 CHECKED_LIST_EXCLUDE
,因为示例中没有;如果它也可以有多个值,那么您可能需要为此做类似的事情。
尝试使用 string-join
函数:'string-join(Values//Value, ", ")'
.
您可以使用 FLOWR 将 XML 重写为在每个 Value
元素中包含尾随 ,
:
SELECT s.SUBSCRIPTION_ID,
s.PARAMETERS_XML,
xt.User_Param,
RTRIM(xt.Param_Value, ',') AS Param_Value,
xt.Checked_List_Exclude
FROM EFNTA.SUBSCRIPTIONS s
CROSS JOIN
XMLTABLE(
'copy $e := .
modify (
for $i in $e/Model/Parameters/Parameter/Values/Value
return replace node $i with <Value>{$i},</Value>
)
return $e/Model/Parameters/Parameter'
PASSING XMLTYPE(s.PARAMETERS_XML)
COLUMNS
USER_PARAM VARCHAR2(100) PATH 'userParameter',
PARAM_VALUE VARCHAR2(1000) PATH '//*:Values',
CHECKED_LIST_EXCLUDE VARCHAR2(100) PATH '//*:CheckedListExclude'
) xt
ORDER BY 1 DESC;
或者,按照 string-join()
:
SELECT s.SUBSCRIPTION_ID,
s.PARAMETERS_XML,
xt.*
FROM EFNTA.SUBSCRIPTIONS s
CROSS JOIN
XMLTABLE(
'/Model/Parameters/Parameter'
PASSING XMLTYPE(s.PARAMETERS_XML)
COLUMNS
USER_PARAM VARCHAR2(100) PATH 'userParameter',
PARAM_VALUE VARCHAR2(1000) PATH 'Values/string-join(./Value/text(), ",")',
CHECKED_LIST_EXCLUDE VARCHAR2(100) PATH '//CheckedListExclude'
) xt
ORDER BY 1 DESC;
这两个输出:
SUBSCRIPTION_ID | PARAMETERS_XML | USER_PARAM | PARAM_VALUE | CHECKED_LIST_EXCLUDE |
---|---|---|---|---|
1 | <Model <Parameters> <Parameter> <userParameter>outlets</userParameter> <Values> <Value>45</Value> <Value>676</Value> <Value>1502</Value> </Values> </Parameter> </Parameters> </Model> |
outlets | 45,676,1502 | null |
db<>fiddle here
我终于这样处理了:
SELECT
s.SUBSCRIPTION_ID,
s.PARAMETERS_XML,
xt.USER_PARAM,
xt.CHECKED_LIST_EXCLUDE,
NVL(xt.PARAM_VALUE, xt.PARAM_VALUES) AS PARAM_VALUES
FROM EFNTA.SUBSCRIPTIONS s,
XMLTABLE(
'/Model/Parameters/Parameter'
PASSING XMLTYPE(s.PARAMETERS_XML)
COLUMNS USER_PARAM VARCHAR2(100) PATH 'userParameter',
CHECKED_LIST_EXCLUDE VARCHAR2(100) PATH '//*:CheckedListExclude',
PARAM_VALUE VARCHAR2(4000) PATH 'Value',
PARAM_VALUES VARCHAR2(4000) PATH 'string-join(Values//Value,";")'
) xt ORDER BY 1 DESC;
我不得不放另一列和一个 NVL,因为我注意到有时我在节点正下方有一个节点,并且有所需的输出:
感谢您的建议,我会保密的,以防万一:-)