如何在 Oracle 中使用 XMLTABLE 内部连接三个表
How to inner join three tables with XMLTABLE in Oracle
我有一个名为 XML_INFRASTRUCTURE
的 table,它具有以下设计:
COLUMN_NAME | DATA_TYPE | NULLABLE
-------------|--------------------|--------
XMLI_ID | NUMBER(10,0) | No
FILENAME | VARCHAR2(255 CHAR) | Yes
LAST_VERSION | DATE | Yes
XML_RAW | CLOB | Yes
在此 table 中,我将通过 FTP 接收的所有 XML 文件存储为 XMLTYPE。我使用 XMLTABLE 来获取信息并且一切正常,直到我开始加入 tables.
在 XML_INFRASTRUCTURE
我有以下数据:
XMLI_ID | FILENAME | LAST_VERSION | XML_RAW
--------|--------------|--------------|--------------------------------
1 | ptcar | 07-JAN-18 | <?xml version="1.0" encoding="ISO-8859-1"?><cern:ptcars creationDate="2018-03-16T19:35:54" xmlns:cern="http://www.website.com/Infrastructure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.website.com/Infrastructure ../ns/infrastructure.xsd"><cern:ptcar id="1" validFromDate="1996-06-02" validToDate="2007-12-08" ....>
2 | ptrefColumn | 07-JAN-18 | <?xml version="1.0" encoding="ISO-8859-1"?><cern:ptrefColumns creationDate="2018-03-20T11:33:21" xmlns:cern="http://www.website.com/Infrastructure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.website.com/Infrastructure ../ns/infrastructure.xsd"><cern:ptrefColumn id="279" validFromDate="1998-04-01" validToDate="2001-06-11" ....>
3 | ptref | 07-JAN-18 | <?xml version="1.0" encoding="ISO-8859-1"?><cern:ptrefs creationDate="2018-03-20T11:33:05" xmlns:cern="http://www.website.com/Infrastructure"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.website.com//Infrastructure ../ns/infrastructure.xsd"><cern:ptref id="232" validFromDate="1998-04-01" validToDate="2001-06-11" ....>
接下来我有以下要求:
给定值:ptrefId 和 givenDate
select a.longNameFrench , a.longNameDutch
from ptcar as a,
ptrefColumn as b,
ptref as c
where c.id = ptrefId
and b.id = c.ptrefColumnId
and a.id = b.ptcarId
and a.validFromDate <= givenDate
and a.validToDate >= givenDate
and b.validFromDate <= givenDate
and b.validToDate >= givenDate
and c.validFromDate <= givenDate
and c.validToDate >= givenDate
所以知道这一点后,我尝试使用 XMLTABLE 获取 XML,但我不知道如何获取连接和 运行。如您所见,我尝试链接 XMLTABLE,但像这样它已经 运行 一个多小时了。
SELECT X.LongNameFrench, X.LongNameDutch
FROM XML_Infrastructure,
XMLTABLE(
'$d/*:ptcars/*:ptcar'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Id VARCHAR2(10) PATH '@*:id',
LongNameFrench VARCHAR2(60) PATH '@*:longNameFrench',
LongNameDutch VARCHAR2(60) PATH '@*:longNameDutch',
ValidFromDate VARCHAR2(10) PATH '@*:validFromDate',
ValidToDate VARCHAR2(10) PATH '@*:validToDate'
) AS X,
XMLTABLE(
'$d/*:ptrefColumns/*:ptrefColumn'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Id VARCHAR2(10) PATH '@*:id',
ValidFromDate VARCHAR2(10) PATH '@*:validFromDate',
ValidToDate VARCHAR2(10) PATH '@*:validToDate'
) AS Y,
XMLTABLE(
'$d/*:ptrefs/*:ptref'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Id VARCHAR2(10) PATH '@*:id',
ValidFromDate VARCHAR2(10) PATH '@*:validFromDate',
ValidToDate VARCHAR2(10) PATH '@*:validToDate'
) AS Z
WHERE Z.Id = '512'
AND FILENAME = 'ptcar';
非常欢迎提出建议! (抱歉信息过载)
像这样制作 select(或创建视图):
CREATE VIEW ptcar AS
SELECT Id, LongNameFrench, LongNameDutch,
TO_DATE(x.ValidFromDate, 'YYYY-MM-DD') as ValidFromDate,
...
FROM XML_Infrastructure,
XMLTABLE(
'$d/*:ptcars/*:ptcar'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Id VARCHAR2(10) PATH '@*:id',
LongNameFrench VARCHAR2(60) PATH '@*:longNameFrench',
LongNameDutch VARCHAR2(60) PATH '@*:longNameDutch',
ValidFromDate VARCHAR2(10) PATH '@*:validFromDate',
ValidToDate VARCHAR2(10) PATH '@*:validToDate'
) as x;
CREATE VIEW ptrefColumn AS
SELECT Id,
TO_DATE(x.ValidFromDate, 'YYYY-MM-DD') as ValidFromDate,
...
FROM XML_Infrastructure,
XMLTABLE(
'$d/*:ptrefColumns/*:ptrefColumn'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Id VARCHAR2(10) PATH '@*:id',
ValidFromDate VARCHAR2(10) PATH '@*:validFromDate',
ValidToDate VARCHAR2(10) PATH '@*:validToDate'
) as x
之后您可以按照您的问题直接加入他们。
注意,为什么将 XML_RAW
存储为 CLOB
而不是 XMLTYPE
?
您正在从同一个 XML 文档创建三个 XMLTable 结果 X、Y 和 Z,并且只有 ptcar
节点 - 所以 Y 和 Z 没有找不到任何数据(因为没有与这些 XPath 匹配的节点)。
考虑到三个 XML 文档之间的相似性,并假设您显示的 ID 节点都应该是相同的值(在您的示例数据中不是这种情况),您可以使用单个 XML 表从所有文档中提取所有相关数据:
SELECT X.Name, X.Id, X.ValidFromDate, X.ValidToDate, X.LongNameFrench, X.LongNameDutch
FROM XML_Infrastructure
CROSS JOIN XMLTABLE(
'$d/*/*'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Name VARCHAR2(10) PATH './local-name()',
Id NUMBER PATH '@*:id',
LongNameFrench VARCHAR2(60) PATH '@*:longNameFrench',
LongNameDutch VARCHAR2(60) PATH '@*:longNameDutch',
ValidFromDate DATE PATH '@*:validFromDate',
ValidToDate DATE PATH '@*:validToDate'
) X;
这使用通配符来获取任何子节点,但如果您有其他未显示的类型,您可以对其进行过滤。它还添加了一个 name
列,这样您就可以知道每一行来自哪个文档(或者您可以根据需要包含文件名)。它将为所有三个中都不存在的属性提供空值。
然后在 CTE 中使用它并将其连接到自身两次:
WITH cte AS (
SELECT X.Name, X.Id, X.ValidFromDate, X.ValidToDate, X.LongNameFrench, X.LongNameDutch
FROM XML_Infrastructure
CROSS JOIN XMLTABLE(
'$d/*/*'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Name VARCHAR2(30) PATH './local-name()',
Id NUMBER PATH '@*:id',
LongNameFrench VARCHAR2(60) PATH '@*:longNameFrench',
LongNameDutch VARCHAR2(60) PATH '@*:longNameDutch',
ValidFromDate DATE PATH '@*:validFromDate',
ValidToDate DATE PATH '@*:validToDate'
) AS X
)
select a.longNameFrench, a.longNameDutch
from cte a
join cte b on b.id = a.id
join cte c on c.id = b.id
where c.name = 'ptref'
and b.name = 'ptrefColumn'
and a.name = 'ptcar'
and c.id = ptrefId
and a.validFromDate <= givenDate
and a.validToDate >= givenDate
and b.validFromDate <= givenDate
and b.validToDate >= givenDate
and c.validFromDate <= givenDate
and c.validToDate >= givenDate;
这有点类似于为每种文档类型创建查询视图,然后加入这些视图,但不需要任何新的永久对象。
在另一个 CTE 中使用您的部分示例数据,并将所有 ID 设置为 512 并在 ptcar
中添加缺少的名称:
with XML_INFRASTRUCTURE (XMLI_ID, FILENAME, LAST_VERSION, XML_RAW) as (
select cast (1 as number(2,0)), cast('ptcar' as varchar2(255)), date '2018-01-07', to_clob('<?xml version="1.0" encoding="ISO-8859-1"?>
<cern:ptcars creationDate="2018-03-16T19:35:54" xmlns:cern="http://www.website.com/Infrastructure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.website.com/Infrastructure ../ns/infrastructure.xsd">
<cern:ptcar id="512" validFromDate="1996-06-02" validToDate="2007-12-08" longNameFrench="Jean Dupont" longNameDutch="Jan Jansen"/>
</cern:ptcars>') from dual
union all select 2, 'ptrefColumn', date '2018-01-07', to_clob('<?xml version="1.0" encoding="ISO-8859-1"?>
<cern:ptrefColumns creationDate="2018-03-20T11:33:21" xmlns:cern="http://www.website.com/Infrastructure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.website.com/Infrastructure ../ns/infrastructure.xsd">
<cern:ptrefColumn id="512" validFromDate="1998-04-01" validToDate="2001-06-11" />
</cern:ptrefColumns>') from dual
union all select 3, 'ptref', date '2018-01-07', to_clob('<?xml version="1.0" encoding="ISO-8859-1"?>
<cern:ptrefs creationDate="2018-03-20T11:33:05" xmlns:cern="http://www.website.com/Infrastructure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.website.com//Infrastructure ../ns/infrastructure.xsd">
<cern:ptref id="512" validFromDate="1998-04-01" validToDate="2001-06-11" />
</cern:ptrefs>') from dual
),
cte AS (
SELECT X.Name, X.Id, X.ValidFromDate, X.ValidToDate, X.LongNameFrench, X.LongNameDutch
FROM XML_Infrastructure
CROSS JOIN XMLTABLE(
'$d/*/*'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Name VARCHAR2(30) PATH './local-name()',
Id NUMBER PATH '@*:id',
LongNameFrench VARCHAR2(60) PATH '@*:longNameFrench',
LongNameDutch VARCHAR2(60) PATH '@*:longNameDutch',
ValidFromDate DATE PATH '@*:validFromDate',
ValidToDate DATE PATH '@*:validToDate'
) AS X
)
select a.longNameFrench, a.longNameDutch
from cte a
join cte b on b.id = a.id
join cte c on c.id = b.id
where c.name = 'ptref'
and b.name = 'ptrefColumn'
and a.name = 'ptcar'
and c.id = 512
and a.validFromDate <= date '2001-01-01'
and a.validToDate >= date '2001-01-01'
and b.validFromDate <= date '2001-01-01'
and b.validToDate >= date '2001-01-01'
and c.validFromDate <= date '2001-01-01'
and c.validToDate >= date '2001-01-01';
给予
LONGNAMEFRENCH LONGNAMEDUTCH
------------------------------------------------------------ ------------------------------------------------------------
Jean Dupont Jan Jansen
我有一个名为 XML_INFRASTRUCTURE
的 table,它具有以下设计:
COLUMN_NAME | DATA_TYPE | NULLABLE
-------------|--------------------|--------
XMLI_ID | NUMBER(10,0) | No
FILENAME | VARCHAR2(255 CHAR) | Yes
LAST_VERSION | DATE | Yes
XML_RAW | CLOB | Yes
在此 table 中,我将通过 FTP 接收的所有 XML 文件存储为 XMLTYPE。我使用 XMLTABLE 来获取信息并且一切正常,直到我开始加入 tables.
在 XML_INFRASTRUCTURE
我有以下数据:
XMLI_ID | FILENAME | LAST_VERSION | XML_RAW
--------|--------------|--------------|--------------------------------
1 | ptcar | 07-JAN-18 | <?xml version="1.0" encoding="ISO-8859-1"?><cern:ptcars creationDate="2018-03-16T19:35:54" xmlns:cern="http://www.website.com/Infrastructure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.website.com/Infrastructure ../ns/infrastructure.xsd"><cern:ptcar id="1" validFromDate="1996-06-02" validToDate="2007-12-08" ....>
2 | ptrefColumn | 07-JAN-18 | <?xml version="1.0" encoding="ISO-8859-1"?><cern:ptrefColumns creationDate="2018-03-20T11:33:21" xmlns:cern="http://www.website.com/Infrastructure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.website.com/Infrastructure ../ns/infrastructure.xsd"><cern:ptrefColumn id="279" validFromDate="1998-04-01" validToDate="2001-06-11" ....>
3 | ptref | 07-JAN-18 | <?xml version="1.0" encoding="ISO-8859-1"?><cern:ptrefs creationDate="2018-03-20T11:33:05" xmlns:cern="http://www.website.com/Infrastructure"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.website.com//Infrastructure ../ns/infrastructure.xsd"><cern:ptref id="232" validFromDate="1998-04-01" validToDate="2001-06-11" ....>
接下来我有以下要求:
给定值:ptrefId 和 givenDate
select a.longNameFrench , a.longNameDutch
from ptcar as a,
ptrefColumn as b,
ptref as c
where c.id = ptrefId
and b.id = c.ptrefColumnId
and a.id = b.ptcarId
and a.validFromDate <= givenDate
and a.validToDate >= givenDate
and b.validFromDate <= givenDate
and b.validToDate >= givenDate
and c.validFromDate <= givenDate
and c.validToDate >= givenDate
所以知道这一点后,我尝试使用 XMLTABLE 获取 XML,但我不知道如何获取连接和 运行。如您所见,我尝试链接 XMLTABLE,但像这样它已经 运行 一个多小时了。
SELECT X.LongNameFrench, X.LongNameDutch
FROM XML_Infrastructure,
XMLTABLE(
'$d/*:ptcars/*:ptcar'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Id VARCHAR2(10) PATH '@*:id',
LongNameFrench VARCHAR2(60) PATH '@*:longNameFrench',
LongNameDutch VARCHAR2(60) PATH '@*:longNameDutch',
ValidFromDate VARCHAR2(10) PATH '@*:validFromDate',
ValidToDate VARCHAR2(10) PATH '@*:validToDate'
) AS X,
XMLTABLE(
'$d/*:ptrefColumns/*:ptrefColumn'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Id VARCHAR2(10) PATH '@*:id',
ValidFromDate VARCHAR2(10) PATH '@*:validFromDate',
ValidToDate VARCHAR2(10) PATH '@*:validToDate'
) AS Y,
XMLTABLE(
'$d/*:ptrefs/*:ptref'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Id VARCHAR2(10) PATH '@*:id',
ValidFromDate VARCHAR2(10) PATH '@*:validFromDate',
ValidToDate VARCHAR2(10) PATH '@*:validToDate'
) AS Z
WHERE Z.Id = '512'
AND FILENAME = 'ptcar';
非常欢迎提出建议! (抱歉信息过载)
像这样制作 select(或创建视图):
CREATE VIEW ptcar AS
SELECT Id, LongNameFrench, LongNameDutch,
TO_DATE(x.ValidFromDate, 'YYYY-MM-DD') as ValidFromDate,
...
FROM XML_Infrastructure,
XMLTABLE(
'$d/*:ptcars/*:ptcar'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Id VARCHAR2(10) PATH '@*:id',
LongNameFrench VARCHAR2(60) PATH '@*:longNameFrench',
LongNameDutch VARCHAR2(60) PATH '@*:longNameDutch',
ValidFromDate VARCHAR2(10) PATH '@*:validFromDate',
ValidToDate VARCHAR2(10) PATH '@*:validToDate'
) as x;
CREATE VIEW ptrefColumn AS
SELECT Id,
TO_DATE(x.ValidFromDate, 'YYYY-MM-DD') as ValidFromDate,
...
FROM XML_Infrastructure,
XMLTABLE(
'$d/*:ptrefColumns/*:ptrefColumn'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Id VARCHAR2(10) PATH '@*:id',
ValidFromDate VARCHAR2(10) PATH '@*:validFromDate',
ValidToDate VARCHAR2(10) PATH '@*:validToDate'
) as x
之后您可以按照您的问题直接加入他们。
注意,为什么将 XML_RAW
存储为 CLOB
而不是 XMLTYPE
?
您正在从同一个 XML 文档创建三个 XMLTable 结果 X、Y 和 Z,并且只有 ptcar
节点 - 所以 Y 和 Z 没有找不到任何数据(因为没有与这些 XPath 匹配的节点)。
考虑到三个 XML 文档之间的相似性,并假设您显示的 ID 节点都应该是相同的值(在您的示例数据中不是这种情况),您可以使用单个 XML 表从所有文档中提取所有相关数据:
SELECT X.Name, X.Id, X.ValidFromDate, X.ValidToDate, X.LongNameFrench, X.LongNameDutch
FROM XML_Infrastructure
CROSS JOIN XMLTABLE(
'$d/*/*'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Name VARCHAR2(10) PATH './local-name()',
Id NUMBER PATH '@*:id',
LongNameFrench VARCHAR2(60) PATH '@*:longNameFrench',
LongNameDutch VARCHAR2(60) PATH '@*:longNameDutch',
ValidFromDate DATE PATH '@*:validFromDate',
ValidToDate DATE PATH '@*:validToDate'
) X;
这使用通配符来获取任何子节点,但如果您有其他未显示的类型,您可以对其进行过滤。它还添加了一个 name
列,这样您就可以知道每一行来自哪个文档(或者您可以根据需要包含文件名)。它将为所有三个中都不存在的属性提供空值。
然后在 CTE 中使用它并将其连接到自身两次:
WITH cte AS (
SELECT X.Name, X.Id, X.ValidFromDate, X.ValidToDate, X.LongNameFrench, X.LongNameDutch
FROM XML_Infrastructure
CROSS JOIN XMLTABLE(
'$d/*/*'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Name VARCHAR2(30) PATH './local-name()',
Id NUMBER PATH '@*:id',
LongNameFrench VARCHAR2(60) PATH '@*:longNameFrench',
LongNameDutch VARCHAR2(60) PATH '@*:longNameDutch',
ValidFromDate DATE PATH '@*:validFromDate',
ValidToDate DATE PATH '@*:validToDate'
) AS X
)
select a.longNameFrench, a.longNameDutch
from cte a
join cte b on b.id = a.id
join cte c on c.id = b.id
where c.name = 'ptref'
and b.name = 'ptrefColumn'
and a.name = 'ptcar'
and c.id = ptrefId
and a.validFromDate <= givenDate
and a.validToDate >= givenDate
and b.validFromDate <= givenDate
and b.validToDate >= givenDate
and c.validFromDate <= givenDate
and c.validToDate >= givenDate;
这有点类似于为每种文档类型创建查询视图,然后加入这些视图,但不需要任何新的永久对象。
在另一个 CTE 中使用您的部分示例数据,并将所有 ID 设置为 512 并在 ptcar
中添加缺少的名称:
with XML_INFRASTRUCTURE (XMLI_ID, FILENAME, LAST_VERSION, XML_RAW) as (
select cast (1 as number(2,0)), cast('ptcar' as varchar2(255)), date '2018-01-07', to_clob('<?xml version="1.0" encoding="ISO-8859-1"?>
<cern:ptcars creationDate="2018-03-16T19:35:54" xmlns:cern="http://www.website.com/Infrastructure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.website.com/Infrastructure ../ns/infrastructure.xsd">
<cern:ptcar id="512" validFromDate="1996-06-02" validToDate="2007-12-08" longNameFrench="Jean Dupont" longNameDutch="Jan Jansen"/>
</cern:ptcars>') from dual
union all select 2, 'ptrefColumn', date '2018-01-07', to_clob('<?xml version="1.0" encoding="ISO-8859-1"?>
<cern:ptrefColumns creationDate="2018-03-20T11:33:21" xmlns:cern="http://www.website.com/Infrastructure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.website.com/Infrastructure ../ns/infrastructure.xsd">
<cern:ptrefColumn id="512" validFromDate="1998-04-01" validToDate="2001-06-11" />
</cern:ptrefColumns>') from dual
union all select 3, 'ptref', date '2018-01-07', to_clob('<?xml version="1.0" encoding="ISO-8859-1"?>
<cern:ptrefs creationDate="2018-03-20T11:33:05" xmlns:cern="http://www.website.com/Infrastructure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.website.com//Infrastructure ../ns/infrastructure.xsd">
<cern:ptref id="512" validFromDate="1998-04-01" validToDate="2001-06-11" />
</cern:ptrefs>') from dual
),
cte AS (
SELECT X.Name, X.Id, X.ValidFromDate, X.ValidToDate, X.LongNameFrench, X.LongNameDutch
FROM XML_Infrastructure
CROSS JOIN XMLTABLE(
'$d/*/*'
PASSING XMLTYPE(XML_Infrastructure.XML_RAW) as "d"
COLUMNS
Name VARCHAR2(30) PATH './local-name()',
Id NUMBER PATH '@*:id',
LongNameFrench VARCHAR2(60) PATH '@*:longNameFrench',
LongNameDutch VARCHAR2(60) PATH '@*:longNameDutch',
ValidFromDate DATE PATH '@*:validFromDate',
ValidToDate DATE PATH '@*:validToDate'
) AS X
)
select a.longNameFrench, a.longNameDutch
from cte a
join cte b on b.id = a.id
join cte c on c.id = b.id
where c.name = 'ptref'
and b.name = 'ptrefColumn'
and a.name = 'ptcar'
and c.id = 512
and a.validFromDate <= date '2001-01-01'
and a.validToDate >= date '2001-01-01'
and b.validFromDate <= date '2001-01-01'
and b.validToDate >= date '2001-01-01'
and c.validFromDate <= date '2001-01-01'
and c.validToDate >= date '2001-01-01';
给予
LONGNAMEFRENCH LONGNAMEDUTCH
------------------------------------------------------------ ------------------------------------------------------------
Jean Dupont Jan Jansen