如何在 Oracle 的 XMLTABLE 中包含对象列表?
How to include a list of objects in Oracle's 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 的 table。也就是说,我想要以下输出:
+----------------+-----+-------------+
| 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 returns value of only one node
我怎样才能达到预期的输出?这里有一个 SQLFiddle 供您试用(如有必要)。
您可以使用链式 XML表调用:
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 doesn't like that, but db<>fiddle does, and it works locally against 11gR2. (Actually SQL Fiddle is OK with a real table instead of a 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,你也可以用一个XML表来做到这一点,方法是从数字开始,然后在节点上查找其他数据:
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
SQL Fiddle and db<>fiddle.
但您的真实(非最小)XML 可能不实用。
这也适用于具有多行的 tables,而不仅仅是 CTE 或 table 具有要解包的单个 XML 值。
如果遇到 list_of_names
缺失或为空的情况,而您仍想显示 name/age,则可以使用外部联接而不是交叉联接,但它需要一个丑陋的 on 1=1
子句。 SQL Fiddle 显示了这种数据的交叉连接和左连接,但如果可以的话,我会避免这种左连接方法。
如果您使用的是 12c 或更高版本,您可以使用 outer apply
而不是 left join ... on 1=1
,这样会减少攻击性。 (如果您不必担心丢失数字,您可以使用 cross apply
而不是 @Lukasz 显示的 cross join
- 这里似乎没有什么区别。)
ORA-19025 很有趣。 SQL Fiddle 是 运行 Oracle 数据库 11g 快捷版版本 11.2.0.2.0。在企业版 11.2.0.4 中,您的代码得到
ORA-19279: XPTY0004 - XQuery dynamic type mismatch: expected singleton sequence - got multi-item sequence
相反,但这是同一个问题;每个人下面有多个 number
节点,作为默认数据类型,它不知道如何处理它们——因为您没有指定数据类型,所有内容都以字符串形式返回。在我的第一个版本中,我使用相同的路径,但将该列声明为 XMLType,因此您在第一个版本中将数字作为 XML 片段获得:
<number>1</number><number>2</number>
或第二个:
<list_of_numbers><number>1</number><number>2</number></list_of_numbers>
然后可以通过链接的 XMLTable 调用使用它们。
如果你有不止一行,你可以很容易地 "chain" XMLTABLE with CROSS/OUTER APPLY
:
select t.id, t1.Name, t1.Age, t2."number"
from t
CROSS APPLY xmltable('/root/person' passing xmltype(t.my_xml)
columns name path 'name', age path 'age',
list_of_numbers xmltype path 'list_of_numbers/number') t1
CROSS APPLY xmltable('/number' passing t1.list_of_numbers
columns "number" number path '.') t2
Main table 是 t,t1 指使用 xmltype(t.my_xml) ,t2 指使用 t1.list_of_numbers.
解析的 XML
附录:
当所有 XMLTABLE 产生行时,CROSS APPLY 和 CROSS JOIN 是等效的:
declare
x VARCHAR2(2000);
begin
dbms_utility.expand_sql_text(
input_sql_text => q'{
select t.id, t1.Name, t1.Age, t2."number"
from t
CROSS JOIN xmltable('/root/person' passing xmltype(t.my_xml) columns name path 'name', age path 'age', list_of_numbers xmltype path 'list_of_numbers/number') t1
CROSS JOIN xmltable('/number' passing t1.list_of_numbers columns "number" number path '.') t2
}',
output_sql_text => x);
dbms_output.put_line(x);
end;
/
declare
x VARCHAR2(2000);
begin
dbms_utility.expand_sql_text(
input_sql_text => q'{
select t.id, t1.Name, t1.Age, t2."number"
from t
CROSS APPLY xmltable('/root/person' passing xmltype(t.my_xml) columns name path 'name', age path 'age', list_of_numbers xmltype path 'list_of_numbers/number') t1
CROSS APPLY xmltable('/number' passing t1.list_of_numbers columns "number" number path '.') t2
}',
output_sql_text => x);
dbms_output.put_line(x);
end;
/
当我们有 XML 时,差异是可见的:
<root>
<person>
<name>XXXX</name>
<age>32</age>
</person>
</root>
select t.id, t1.Name, t1.Age, t2."number"
from t
OUTER APPLY xmltable('/root/person' passing xmltype(t.my_xml) columns name path 'name', age path 'age', list_of_numbers xmltype path 'list_of_numbers/number') t1
OUTER APPLY xmltable('/number' passing t1.list_of_numbers columns "number" number path '.') t2
交叉连接 - 0 行
外部应用 - 1 行
我有以下(最小)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 的 table。也就是说,我想要以下输出:
+----------------+-----+-------------+
| 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 returns value of only one node
我怎样才能达到预期的输出?这里有一个 SQLFiddle 供您试用(如有必要)。
您可以使用链式 XML表调用:
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 doesn't like that, but db<>fiddle does, and it works locally against 11gR2. (Actually SQL Fiddle is OK with a real table instead of a 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,你也可以用一个XML表来做到这一点,方法是从数字开始,然后在节点上查找其他数据:
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
SQL Fiddle and db<>fiddle.
但您的真实(非最小)XML 可能不实用。
这也适用于具有多行的 tables,而不仅仅是 CTE 或 table 具有要解包的单个 XML 值。
如果遇到 list_of_names
缺失或为空的情况,而您仍想显示 name/age,则可以使用外部联接而不是交叉联接,但它需要一个丑陋的 on 1=1
子句。 SQL Fiddle 显示了这种数据的交叉连接和左连接,但如果可以的话,我会避免这种左连接方法。
如果您使用的是 12c 或更高版本,您可以使用 outer apply
而不是 left join ... on 1=1
,这样会减少攻击性。 (如果您不必担心丢失数字,您可以使用 cross apply
而不是 @Lukasz 显示的 cross join
- 这里似乎没有什么区别。)
ORA-19025 很有趣。 SQL Fiddle 是 运行 Oracle 数据库 11g 快捷版版本 11.2.0.2.0。在企业版 11.2.0.4 中,您的代码得到
ORA-19279: XPTY0004 - XQuery dynamic type mismatch: expected singleton sequence - got multi-item sequence
相反,但这是同一个问题;每个人下面有多个 number
节点,作为默认数据类型,它不知道如何处理它们——因为您没有指定数据类型,所有内容都以字符串形式返回。在我的第一个版本中,我使用相同的路径,但将该列声明为 XMLType,因此您在第一个版本中将数字作为 XML 片段获得:
<number>1</number><number>2</number>
或第二个:
<list_of_numbers><number>1</number><number>2</number></list_of_numbers>
然后可以通过链接的 XMLTable 调用使用它们。
如果你有不止一行,你可以很容易地 "chain" XMLTABLE with CROSS/OUTER APPLY
:
select t.id, t1.Name, t1.Age, t2."number"
from t
CROSS APPLY xmltable('/root/person' passing xmltype(t.my_xml)
columns name path 'name', age path 'age',
list_of_numbers xmltype path 'list_of_numbers/number') t1
CROSS APPLY xmltable('/number' passing t1.list_of_numbers
columns "number" number path '.') t2
Main table 是 t,t1 指使用 xmltype(t.my_xml) ,t2 指使用 t1.list_of_numbers.
解析的 XML附录:
当所有 XMLTABLE 产生行时,CROSS APPLY 和 CROSS JOIN 是等效的:
declare
x VARCHAR2(2000);
begin
dbms_utility.expand_sql_text(
input_sql_text => q'{
select t.id, t1.Name, t1.Age, t2."number"
from t
CROSS JOIN xmltable('/root/person' passing xmltype(t.my_xml) columns name path 'name', age path 'age', list_of_numbers xmltype path 'list_of_numbers/number') t1
CROSS JOIN xmltable('/number' passing t1.list_of_numbers columns "number" number path '.') t2
}',
output_sql_text => x);
dbms_output.put_line(x);
end;
/
declare
x VARCHAR2(2000);
begin
dbms_utility.expand_sql_text(
input_sql_text => q'{
select t.id, t1.Name, t1.Age, t2."number"
from t
CROSS APPLY xmltable('/root/person' passing xmltype(t.my_xml) columns name path 'name', age path 'age', list_of_numbers xmltype path 'list_of_numbers/number') t1
CROSS APPLY xmltable('/number' passing t1.list_of_numbers columns "number" number path '.') t2
}',
output_sql_text => x);
dbms_output.put_line(x);
end;
/
当我们有 XML 时,差异是可见的:
<root>
<person>
<name>XXXX</name>
<age>32</age>
</person>
</root>
select t.id, t1.Name, t1.Age, t2."number"
from t
OUTER APPLY xmltable('/root/person' passing xmltype(t.my_xml) columns name path 'name', age path 'age', list_of_numbers xmltype path 'list_of_numbers/number') t1
OUTER APPLY xmltable('/number' passing t1.list_of_numbers columns "number" number path '.') t2
交叉连接 - 0 行
外部应用 - 1 行