简体   繁体   English

如何在Oracle中使用XMLTABLE内部联接三个表

[英]How to inner join three tables with XMLTABLE in Oracle

I have a table called XML_INFRASTRUCTURE which has the following design: 我有一个名为XML_INFRASTRUCTURE的表,该表具有以下设计:

COLUMN_NAME  | DATA_TYPE          | NULLABLE
-------------|--------------------|--------
XMLI_ID      | NUMBER(10,0)       | No
FILENAME     | VARCHAR2(255 CHAR) | Yes
LAST_VERSION | DATE               | Yes
XML_RAW      | CLOB               | Yes

In this table I am storing all the XML files I'm receiving through FTP as an XMLTYPE. 在此表中,我将通过FTP接收的所有XML文件存储为XMLTYPE。 I use XMLTABLE to get the information out and it all works well, until I start joining tables. 我使用XMLTABLE来获取信息,并且在开始加入表之前,一切都很好。

In the XML_INFRASTRUCTURE I have the following data: 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" ....>

And next I have the following requirements: 接下来,我有以下要求:

Given values: ptrefId and givenDate 给定值: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

So knowing this, I tried getting the XML out with XMLTABLE, but I have no idea how to get the join up and running. 因此,知道了这一点,我尝试使用XMLTABLE删除XML,但是我不知道如何启动和运行联接。 As you can see I tried chaining the XMLTABLE, but like this it's been running for over an hour now. 如您所见,我尝试链接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';

Suggestions are very welcome! 建议非常欢迎! (Sorry for the overload of information) (对不起,信息过多)

You are creating the three XMLTable results X, Y and Z from the same XML document, and that only has the ptcar node - so Y and Z don't find any data (as there are no nodes matching those XPaths). 您将在同一XML文档中创建三个XMLTable结果X,Y和Z,并且仅具有ptcar节点-因此Y和Z找不到任何数据(因为没有匹配这些XPath的节点)。

Given the similarities between the three XML documents, and assuming the ID nodes you've shown are all supposed to be the same value (which isn't the case in your example data), you could use a single XMLTable to extract all of the relevant data from all of the documents: 鉴于这三个XML文档之间的相似之处,并且假设您所显示的ID节点都应具有相同的值(示例数据中的情况并非如此),则可以使用单个XMLTable提取所有所有文档的相关数据:

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;

This uses wildcards to get any child node, but you coudl filter that if you have other types you haven't shown. 这使用通配符来获取任何子节点,但是您可以过滤掉,如果您有其他类型,则未显示。 It also adds a name column so you can tell which document each row came from (or you could include the file name if you prefer). 它还添加了一个name列,以便您可以知道每一行来自哪个文档(或者,如果愿意,可以包括文件名)。 It will give null values for the attributes that don't exist in all three. 它将为所有三个属性都不存在的属性提供空值。

And then use that in a CTE and join that to itself twice: 然后在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;

This is somewhat similar to creating views of the queries for each document type and then joining those, but without needing any new permanent objects. 这有点类似于为每种文档类型创建查询视图,然后将其合并,但是不需要任何新的永久对象。

With your partial sample data in another CTE, and setting all the IDs to 512 and adding the missing names in ptcar : 将您的部分样本数据放在另一个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';

gives

LONGNAMEFRENCH                                               LONGNAMEDUTCH                                               
------------------------------------------------------------ ------------------------------------------------------------
Jean Dupont                                                  Jan Jansen                                                  

Make a select (or create views) like this: 进行如下选择(或创建视图):

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

Afterwards you can join them directly as given in your question. 之后,您可以按照您的问题直接加入他们。

NB, why do you store XML_RAW as CLOB rather than XMLTYPE ? 注意,为什么将XML_RAW存储为CLOB而不是XMLTYPE

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM